mirror of
https://github.com/arendst/Tasmota.git
synced 2026-07-28 04:15:53 +00:00
Add support for second I2C bus to some sensors
This commit is contained in:
@@ -5,11 +5,17 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
## [15.3.0.2]
|
||||
### Added
|
||||
- Environment sensors CCS811, SGP30 and SGP40 second I2C bus support
|
||||
- Real Time Clocks PCF85063 and PCF85363 second I2C bus support
|
||||
|
||||
### Breaking Changed
|
||||
|
||||
### Changed
|
||||
- ESP8266 redesigned I2C Wire driver to support second I2C bus
|
||||
- Adafruit_BusIO library from v1.11.0 to v1.17.4
|
||||
- Adafruit_CCS811 library from v1.0.0.14 to v1.1.3
|
||||
- Adafruit SGP30 library from v1.2.0 to v2.0.3
|
||||
- Adafruit SGP40 library from v1.1.0 to v1.1.4
|
||||
|
||||
### Fixed
|
||||
- Crash when shutting down Wifi with `Wifi 0` (#24536)
|
||||
|
||||
+4
-4
@@ -45,7 +45,7 @@ Index | Define | Driver | Device | Address(es) | Bus2 | Descrip
|
||||
22 | USE_MCP230xx | xsns_29 | MCP23008 | 0x20 - 0x26 | Yes | 8-bit I/O expander
|
||||
22 | USE_MCP230xx | xsns_29 | MCP23017 | 0x20 - 0x26 | Yes | 16-bit I/O expander
|
||||
23 | USE_MPR121 | xsns_30 | MPR121 | 0x5A - 0x5D | | Proximity capacitive touch sensor
|
||||
24 | USE_CCS811 | xsns_31 | CCS811 | 0x5A | | Gas (TVOC) and air quality sensor
|
||||
24 | USE_CCS811 | xsns_31 | CCS811 | 0x5A | Yes | Gas (TVOC) and air quality sensor
|
||||
24' | USE_CCS811_V2 | xsns_31 | CCS811 | 0x5A - 0x5B | | Gas (TVOC) and air quality sensor
|
||||
25 | USE_MPU6050 | xsns_32 | MPU6050 | 0x68 - 0x69 | | 3-axis gyroscope and temperature sensor
|
||||
26 | USE_DS3231 | xsns_33 | DS1307 | 0x68 | | Real time clock
|
||||
@@ -104,10 +104,10 @@ Index | Define | Driver | Device | Address(es) | Bus2 | Descrip
|
||||
63 | USE_HM330X | xsns_93 | HM330X | 0x40 | | Particule sensor
|
||||
64 | USE_HDC2010 | xsns_94 | HDC2010 | 0x40 | | Temperature and Humidity sensor
|
||||
65 | USE_ADE7880 | xnrg_23 | ADE7880 | 0x38 | | Energy monitor
|
||||
66 | USE_PCF85363 | xsns_99 | PCF85363 | 0x51 | | Real time clock
|
||||
66 | USE_PCF85363 | xsns_99 | PCF85363 | 0x51 | Yes | Real time clock
|
||||
67 | USE_DS3502 | xdrv_61 | DS3502 | 0x28 - 0x2B | | Digital potentiometer
|
||||
68 | USE_HYT | xsns_97 | HYTxxx | 0x28 | Yes | Temperature and Humidity sensor
|
||||
69 | USE_SGP40 | xsns_98 | SGP40 | 0x59 | | Gas (TVOC) and air quality
|
||||
69 | USE_SGP40 | xsns_98 | SGP40 | 0x59 | Yes | Gas (TVOC) and air quality
|
||||
70 | USE_LUXV30B | xsns_99 | LUXV30B | 0x4A | | DFRobot SEN0390 V30B lux sensor
|
||||
71 | USE_QMC5883L | xsns_33 | QMC5883L | 0x0D | | Magnetic Field Sensor
|
||||
72 | USE_INA3221 | xsns_100 | INA3221 | 0x40-0x43 | | 3-channels Voltage and Current sensor
|
||||
@@ -132,7 +132,7 @@ Index | Define | Driver | Device | Address(es) | Bus2 | Descrip
|
||||
90 | USE_RX8010 | xdrv_56 | RX8010 | 0x32 | Yes | RX8010 RTC from IOTTIMER
|
||||
90 | USE_RX8030 | xdrv_56 | RX8030 | 0x32 | Yes | RX8030 RTC from #23855
|
||||
91 | USE_MS5837 | xsns_116 | MS5837 | 0x76 | | Pressure and temperature sensor
|
||||
92 | USE_PCF85063 | xdrv_56 | PCF85063 | 0x51 | | PCF85063 Real time clock
|
||||
92 | USE_PCF85063 | xdrv_56 | PCF85063 | 0x51 | Yes | PCF85063 Real time clock
|
||||
93 | USE_AS33772S | xdrv_119 | AS33772S | 0x52 | Yes | AS33772S USB PD Sink Controller
|
||||
94 | USE_RV3028 | xdrv_56 | RV3028 | 0x52 | Yes | RV-3028-C7 RTC Controller
|
||||
95 | USE_AGS02MA | xsns_118 | AGS02MA | 0x1A | | TVOC Gas sensor
|
||||
|
||||
@@ -117,10 +117,16 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
|
||||
- Support for JSON value pair `"ARCH"` in template being either ESP8266, ESP32, ESP32C2, ESP32C3, ESP32C5, ESP32C6, ESP32H2, ESP32H4, ESP32P4, ESP32S2 or ESP32S3
|
||||
- Support for Sensirion SCD42 and SCD43 CO2 sensor
|
||||
- Support for Sensirion STCC4 CO2 sensor
|
||||
- Environment sensors CCS811, SGP30 and SGP40 second I2C bus support
|
||||
- Real Time Clocks PCF85063 and PCF85363 second I2C bus support
|
||||
- I2S full duplex, auto rx sample rate [#24469](https://github.com/arendst/Tasmota/issues/24469)
|
||||
- Sen5x power on delay of 60ms [#24452](https://github.com/arendst/Tasmota/issues/24452)
|
||||
|
||||
### Changed
|
||||
- Adafruit_BusIO library from v1.11.0 to v1.17.4
|
||||
- Adafruit_CCS811 library from v1.0.0.14 to v1.1.3
|
||||
- Adafruit SGP30 library from v1.2.0 to v2.0.3
|
||||
- Adafruit SGP40 library from v1.1.0 to v1.1.4
|
||||
- Sensirion Core library from v0.6.0 to v0.7.2
|
||||
- LVGL library from v9.4.0 to v9.5.0 [#24470](https://github.com/arendst/Tasmota/issues/24470)
|
||||
- ESP8266 redesigned I2C Wire driver to support second I2C bus
|
||||
|
||||
+39
-20
@@ -21,7 +21,7 @@ Adafruit_BusIO_Register::Adafruit_BusIO_Register(Adafruit_I2CDevice *i2cdevice,
|
||||
uint8_t byteorder,
|
||||
uint8_t address_width) {
|
||||
_i2cdevice = i2cdevice;
|
||||
_spidevice = NULL;
|
||||
_spidevice = nullptr;
|
||||
_addrwidth = address_width;
|
||||
_address = reg_addr;
|
||||
_byteorder = byteorder;
|
||||
@@ -50,7 +50,7 @@ Adafruit_BusIO_Register::Adafruit_BusIO_Register(Adafruit_SPIDevice *spidevice,
|
||||
uint8_t address_width) {
|
||||
_spidevice = spidevice;
|
||||
_spiregtype = type;
|
||||
_i2cdevice = NULL;
|
||||
_i2cdevice = nullptr;
|
||||
_addrwidth = address_width;
|
||||
_address = reg_addr;
|
||||
_byteorder = byteorder;
|
||||
@@ -59,12 +59,12 @@ Adafruit_BusIO_Register::Adafruit_BusIO_Register(Adafruit_SPIDevice *spidevice,
|
||||
|
||||
/*!
|
||||
* @brief Create a register we access over an I2C or SPI Device. This is a
|
||||
* handy function because we can pass in NULL for the unused interface, allowing
|
||||
* libraries to mass-define all the registers
|
||||
* @param i2cdevice The I2CDevice to use for underlying I2C access, if NULL
|
||||
* we use SPI
|
||||
* @param spidevice The SPIDevice to use for underlying SPI access, if NULL
|
||||
* we use I2C
|
||||
* handy function because we can pass in nullptr for the unused interface,
|
||||
* allowing libraries to mass-define all the registers
|
||||
* @param i2cdevice The I2CDevice to use for underlying I2C access, if
|
||||
* nullptr we use SPI
|
||||
* @param spidevice The SPIDevice to use for underlying SPI access, if
|
||||
* nullptr we use I2C
|
||||
* @param reg_addr The address pointer value for the I2C/SMBus/SPI register,
|
||||
* can be 8 or 16 bits
|
||||
* @param type The method we use to read/write data to SPI (which is not
|
||||
@@ -88,6 +88,26 @@ Adafruit_BusIO_Register::Adafruit_BusIO_Register(
|
||||
_width = width;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Create a register we access over a GenericDevice
|
||||
* @param genericdevice Generic device to use
|
||||
* @param reg_addr Register address we will read/write
|
||||
* @param width Width of the register in bytes (1-4)
|
||||
* @param byteorder Byte order of register data (LSBFIRST or MSBFIRST)
|
||||
* @param address_width Width of the register address in bytes (1 or 2)
|
||||
*/
|
||||
Adafruit_BusIO_Register::Adafruit_BusIO_Register(
|
||||
Adafruit_GenericDevice *genericdevice, uint16_t reg_addr, uint8_t width,
|
||||
uint8_t byteorder, uint8_t address_width) {
|
||||
_i2cdevice = nullptr;
|
||||
_spidevice = nullptr;
|
||||
_genericdevice = genericdevice;
|
||||
_addrwidth = address_width;
|
||||
_address = reg_addr;
|
||||
_byteorder = byteorder;
|
||||
_width = width;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Write a buffer of data to the register location
|
||||
* @param buffer Pointer to data to write
|
||||
@@ -96,17 +116,14 @@ Adafruit_BusIO_Register::Adafruit_BusIO_Register(
|
||||
* uncheckable)
|
||||
*/
|
||||
bool Adafruit_BusIO_Register::write(uint8_t *buffer, uint8_t len) {
|
||||
|
||||
uint8_t addrbuffer[2] = {(uint8_t)(_address & 0xFF),
|
||||
(uint8_t)(_address >> 8)};
|
||||
|
||||
if (_i2cdevice) {
|
||||
return _i2cdevice->write(buffer, len, true, addrbuffer, _addrwidth);
|
||||
}
|
||||
if (_spidevice) {
|
||||
if (_spiregtype == ADDRESSED_OPCODE_BIT0_LOW_TO_WRITE) {
|
||||
// very special case!
|
||||
|
||||
// pass the special opcode address which we set as the high byte of the
|
||||
// regaddr
|
||||
addrbuffer[0] =
|
||||
@@ -116,7 +133,6 @@ bool Adafruit_BusIO_Register::write(uint8_t *buffer, uint8_t len) {
|
||||
// the address appears to be a byte longer
|
||||
return _spidevice->write(buffer, len, addrbuffer, _addrwidth + 1);
|
||||
}
|
||||
|
||||
if (_spiregtype == ADDRBIT8_HIGH_TOREAD) {
|
||||
addrbuffer[0] &= ~0x80;
|
||||
}
|
||||
@@ -129,6 +145,9 @@ bool Adafruit_BusIO_Register::write(uint8_t *buffer, uint8_t len) {
|
||||
}
|
||||
return _spidevice->write(buffer, len, addrbuffer, _addrwidth);
|
||||
}
|
||||
if (_genericdevice) {
|
||||
return _genericdevice->writeRegister(addrbuffer, _addrwidth, buffer, len);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -192,23 +211,20 @@ uint32_t Adafruit_BusIO_Register::read(void) {
|
||||
uint32_t Adafruit_BusIO_Register::readCached(void) { return _cached; }
|
||||
|
||||
/*!
|
||||
* @brief Read a buffer of data from the register location
|
||||
* @param buffer Pointer to data to read into
|
||||
* @param len Number of bytes to read
|
||||
* @return True on successful write (only really useful for I2C as SPI is
|
||||
* uncheckable)
|
||||
*/
|
||||
@brief Read a number of bytes from a register into a buffer
|
||||
@param buffer Buffer to read data into
|
||||
@param len Number of bytes to read into the buffer
|
||||
@return true on successful read, otherwise false
|
||||
*/
|
||||
bool Adafruit_BusIO_Register::read(uint8_t *buffer, uint8_t len) {
|
||||
uint8_t addrbuffer[2] = {(uint8_t)(_address & 0xFF),
|
||||
(uint8_t)(_address >> 8)};
|
||||
|
||||
if (_i2cdevice) {
|
||||
return _i2cdevice->write_then_read(addrbuffer, _addrwidth, buffer, len);
|
||||
}
|
||||
if (_spidevice) {
|
||||
if (_spiregtype == ADDRESSED_OPCODE_BIT0_LOW_TO_WRITE) {
|
||||
// very special case!
|
||||
|
||||
// pass the special opcode address which we set as the high byte of the
|
||||
// regaddr
|
||||
addrbuffer[0] =
|
||||
@@ -230,6 +246,9 @@ bool Adafruit_BusIO_Register::read(uint8_t *buffer, uint8_t len) {
|
||||
}
|
||||
return _spidevice->write_then_read(addrbuffer, _addrwidth, buffer, len);
|
||||
}
|
||||
if (_genericdevice) {
|
||||
return _genericdevice->readRegister(addrbuffer, _addrwidth, buffer, len);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+12
@@ -6,6 +6,7 @@
|
||||
#if !defined(SPI_INTERFACES_COUNT) || \
|
||||
(defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0))
|
||||
|
||||
#include <Adafruit_GenericDevice.h>
|
||||
#include <Adafruit_I2CDevice.h>
|
||||
#include <Adafruit_SPIDevice.h>
|
||||
|
||||
@@ -57,6 +58,11 @@ public:
|
||||
uint8_t width = 1, uint8_t byteorder = LSBFIRST,
|
||||
uint8_t address_width = 1);
|
||||
|
||||
Adafruit_BusIO_Register(Adafruit_GenericDevice *genericdevice,
|
||||
uint16_t reg_addr, uint8_t width = 1,
|
||||
uint8_t byteorder = LSBFIRST,
|
||||
uint8_t address_width = 1);
|
||||
|
||||
bool read(uint8_t *buffer, uint8_t len);
|
||||
bool read(uint8_t *value);
|
||||
bool read(uint16_t *value);
|
||||
@@ -71,12 +77,18 @@ public:
|
||||
void setAddress(uint16_t address);
|
||||
void setAddressWidth(uint16_t address_width);
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
void print(Stream *s = &Serial);
|
||||
void println(Stream *s = &Serial);
|
||||
#else
|
||||
void print(Stream *s);
|
||||
void println(Stream *s);
|
||||
#endif
|
||||
|
||||
private:
|
||||
Adafruit_I2CDevice *_i2cdevice;
|
||||
Adafruit_SPIDevice *_spidevice;
|
||||
Adafruit_GenericDevice *_genericdevice;
|
||||
Adafruit_BusIO_SPIRegType _spiregtype;
|
||||
uint16_t _address;
|
||||
uint8_t _width, _addrwidth, _byteorder;
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Written with help by Claude!
|
||||
https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c (at this time
|
||||
chats are not shareable :(
|
||||
*/
|
||||
|
||||
#include "Adafruit_GenericDevice.h"
|
||||
|
||||
/*!
|
||||
* @brief Create a Generic device with the provided read/write functions
|
||||
* @param obj Pointer to object instance
|
||||
* @param read_func Function pointer for reading raw data
|
||||
* @param write_func Function pointer for writing raw data
|
||||
* @param readreg_func Function pointer for reading registers (optional)
|
||||
* @param writereg_func Function pointer for writing registers (optional) */
|
||||
Adafruit_GenericDevice::Adafruit_GenericDevice(
|
||||
void *obj, busio_genericdevice_read_t read_func,
|
||||
busio_genericdevice_write_t write_func,
|
||||
busio_genericdevice_readreg_t readreg_func,
|
||||
busio_genericdevice_writereg_t writereg_func) {
|
||||
_obj = obj;
|
||||
_read_func = read_func;
|
||||
_write_func = write_func;
|
||||
_readreg_func = readreg_func;
|
||||
_writereg_func = writereg_func;
|
||||
_begun = false;
|
||||
}
|
||||
|
||||
/*! @brief Simple begin function (doesn't do much at this time)
|
||||
@return true always
|
||||
*/
|
||||
bool Adafruit_GenericDevice::begin(void) {
|
||||
_begun = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Marks the GenericDevice as no longer in use.
|
||||
@note: Since this is a GenericDevice, if you are using this with a Serial
|
||||
object, this does NOT disable serial communication or release the RX/TX pins.
|
||||
That must be done manually by calling Serial.end().
|
||||
*/
|
||||
void Adafruit_GenericDevice::end(void) { _begun = false; }
|
||||
|
||||
/*! @brief Write a buffer of data
|
||||
@param buffer Pointer to buffer of data to write
|
||||
@param len Number of bytes to write
|
||||
@return true if write was successful, otherwise false */
|
||||
bool Adafruit_GenericDevice::write(const uint8_t *buffer, size_t len) {
|
||||
if (!_begun)
|
||||
return false;
|
||||
return _write_func(_obj, buffer, len);
|
||||
}
|
||||
|
||||
/*! @brief Read data into a buffer
|
||||
@param buffer Pointer to buffer to read data into
|
||||
@param len Number of bytes to read
|
||||
@return true if read was successful, otherwise false */
|
||||
bool Adafruit_GenericDevice::read(uint8_t *buffer, size_t len) {
|
||||
if (!_begun)
|
||||
return false;
|
||||
return _read_func(_obj, buffer, len);
|
||||
}
|
||||
|
||||
/*! @brief Read from a register location
|
||||
@param addr_buf Buffer containing register address
|
||||
@param addrsiz Size of register address in bytes
|
||||
@param buf Buffer to store read data
|
||||
@param bufsiz Size of data to read in bytes
|
||||
@return true if read was successful, otherwise false */
|
||||
bool Adafruit_GenericDevice::readRegister(uint8_t *addr_buf, uint8_t addrsiz,
|
||||
uint8_t *buf, uint16_t bufsiz) {
|
||||
if (!_begun || !_readreg_func)
|
||||
return false;
|
||||
return _readreg_func(_obj, addr_buf, addrsiz, buf, bufsiz);
|
||||
}
|
||||
|
||||
/*! @brief Write to a register location
|
||||
@param addr_buf Buffer containing register address
|
||||
@param addrsiz Size of register address in bytes
|
||||
@param buf Buffer containing data to write
|
||||
@param bufsiz Size of data to write in bytes
|
||||
@return true if write was successful, otherwise false */
|
||||
bool Adafruit_GenericDevice::writeRegister(uint8_t *addr_buf, uint8_t addrsiz,
|
||||
const uint8_t *buf,
|
||||
uint16_t bufsiz) {
|
||||
if (!_begun || !_writereg_func)
|
||||
return false;
|
||||
return _writereg_func(_obj, addr_buf, addrsiz, buf, bufsiz);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef ADAFRUIT_GENERICDEVICE_H
|
||||
#define ADAFRUIT_GENERICDEVICE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
typedef bool (*busio_genericdevice_read_t)(void *obj, uint8_t *buffer,
|
||||
size_t len);
|
||||
typedef bool (*busio_genericdevice_write_t)(void *obj, const uint8_t *buffer,
|
||||
size_t len);
|
||||
typedef bool (*busio_genericdevice_readreg_t)(void *obj, uint8_t *addr_buf,
|
||||
uint8_t addrsiz, uint8_t *data,
|
||||
uint16_t datalen);
|
||||
typedef bool (*busio_genericdevice_writereg_t)(void *obj, uint8_t *addr_buf,
|
||||
uint8_t addrsiz,
|
||||
const uint8_t *data,
|
||||
uint16_t datalen);
|
||||
|
||||
/*!
|
||||
* @brief Class for communicating with a device via generic read/write functions
|
||||
*/
|
||||
class Adafruit_GenericDevice {
|
||||
public:
|
||||
Adafruit_GenericDevice(
|
||||
void *obj, busio_genericdevice_read_t read_func,
|
||||
busio_genericdevice_write_t write_func,
|
||||
busio_genericdevice_readreg_t readreg_func = nullptr,
|
||||
busio_genericdevice_writereg_t writereg_func = nullptr);
|
||||
|
||||
bool begin(void);
|
||||
void end(void);
|
||||
|
||||
bool read(uint8_t *buffer, size_t len);
|
||||
bool write(const uint8_t *buffer, size_t len);
|
||||
bool readRegister(uint8_t *addr_buf, uint8_t addrsiz, uint8_t *buf,
|
||||
uint16_t bufsiz);
|
||||
bool writeRegister(uint8_t *addr_buf, uint8_t addrsiz, const uint8_t *buf,
|
||||
uint16_t bufsiz);
|
||||
|
||||
protected:
|
||||
/*! @brief Function pointer for reading raw data from the device */
|
||||
busio_genericdevice_read_t _read_func;
|
||||
/*! @brief Function pointer for writing raw data to the device */
|
||||
busio_genericdevice_write_t _write_func;
|
||||
/*! @brief Function pointer for reading a 'register' from the device */
|
||||
busio_genericdevice_readreg_t _readreg_func;
|
||||
/*! @brief Function pointer for writing a 'register' to the device */
|
||||
busio_genericdevice_writereg_t _writereg_func;
|
||||
|
||||
bool _begun; ///< whether we have initialized yet (in case the function needs
|
||||
///< to do something)
|
||||
|
||||
private:
|
||||
void *_obj; ///< Pointer to object instance
|
||||
};
|
||||
|
||||
#endif // ADAFRUIT_GENERICDEVICE_H
|
||||
+61
-8
@@ -1,6 +1,6 @@
|
||||
#include "Adafruit_I2CDevice.h"
|
||||
|
||||
//#define DEBUG_SERIAL Serial
|
||||
// #define DEBUG_SERIAL Serial
|
||||
|
||||
/*!
|
||||
* @brief Create an I2C device at a given address
|
||||
@@ -13,6 +13,8 @@ Adafruit_I2CDevice::Adafruit_I2CDevice(uint8_t addr, TwoWire *theWire) {
|
||||
_begun = false;
|
||||
#ifdef ARDUINO_ARCH_SAMD
|
||||
_maxBufferSize = 250; // as defined in Wire.h's RingBuffer
|
||||
#elif defined(ESP32)
|
||||
_maxBufferSize = I2C_BUFFER_LENGTH;
|
||||
#else
|
||||
_maxBufferSize = 32;
|
||||
#endif
|
||||
@@ -21,8 +23,8 @@ Adafruit_I2CDevice::Adafruit_I2CDevice(uint8_t addr, TwoWire *theWire) {
|
||||
/*!
|
||||
* @brief Initializes and does basic address detection
|
||||
* @param addr_detect Whether we should attempt to detect the I2C address
|
||||
* with a scan. 99% of sensors/devices don't mind but once in a while, they spaz
|
||||
* on a scan!
|
||||
* with a scan. 99% of sensors/devices don't mind, but once in a while they
|
||||
* don't respond well to a scan!
|
||||
* @return True if I2C initialized and a device with the addr found
|
||||
*/
|
||||
bool Adafruit_I2CDevice::begin(bool addr_detect) {
|
||||
@@ -65,14 +67,21 @@ bool Adafruit_I2CDevice::detected(void) {
|
||||
|
||||
// A basic scanner, see if it ACK's
|
||||
_wire->beginTransmission(_addr);
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.print(F("Address 0x"));
|
||||
DEBUG_SERIAL.print(_addr, HEX);
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_MBED
|
||||
_wire->write(0); // forces a write request instead of a read
|
||||
#endif
|
||||
if (_wire->endTransmission() == 0) {
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.println(F("Detected"));
|
||||
DEBUG_SERIAL.println(F(" Detected"));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.println(F("Not detected"));
|
||||
DEBUG_SERIAL.println(F(" Not detected"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
@@ -106,7 +115,7 @@ bool Adafruit_I2CDevice::write(const uint8_t *buffer, size_t len, bool stop,
|
||||
_wire->beginTransmission(_addr);
|
||||
|
||||
// Write the prefix data (usually an address)
|
||||
if ((prefix_len != 0) && (prefix_buffer != NULL)) {
|
||||
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
|
||||
if (_wire->write(prefix_buffer, prefix_len) != prefix_len) {
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.println(F("\tI2CDevice failed to write"));
|
||||
@@ -128,7 +137,7 @@ bool Adafruit_I2CDevice::write(const uint8_t *buffer, size_t len, bool stop,
|
||||
DEBUG_SERIAL.print(F("\tI2CWRITE @ 0x"));
|
||||
DEBUG_SERIAL.print(_addr, HEX);
|
||||
DEBUG_SERIAL.print(F(" :: "));
|
||||
if ((prefix_len != 0) && (prefix_buffer != NULL)) {
|
||||
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
|
||||
for (uint16_t i = 0; i < prefix_len; i++) {
|
||||
DEBUG_SERIAL.print(F("0x"));
|
||||
DEBUG_SERIAL.print(prefix_buffer[i], HEX);
|
||||
@@ -187,6 +196,8 @@ bool Adafruit_I2CDevice::read(uint8_t *buffer, size_t len, bool stop) {
|
||||
bool Adafruit_I2CDevice::_read(uint8_t *buffer, size_t len, bool stop) {
|
||||
#if defined(TinyWireM_h)
|
||||
size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len);
|
||||
#elif defined(ARDUINO_ARCH_MEGAAVR)
|
||||
size_t recv = _wire->requestFrom(_addr, len, stop);
|
||||
#else
|
||||
size_t recv = _wire->requestFrom((uint8_t)_addr, (uint8_t)len, (uint8_t)stop);
|
||||
#endif
|
||||
@@ -257,9 +268,51 @@ uint8_t Adafruit_I2CDevice::address(void) { return _addr; }
|
||||
* Not necessarily that the speed was achieved!
|
||||
*/
|
||||
bool Adafruit_I2CDevice::setSpeed(uint32_t desiredclk) {
|
||||
#if (ARDUINO >= 157) && !defined(ARDUINO_STM32_FEATHER) && !defined(TinyWireM_h)
|
||||
#if defined(__AVR_ATmega328__) || \
|
||||
defined(__AVR_ATmega328P__) // fix arduino core set clock
|
||||
// calculate TWBR correctly
|
||||
|
||||
if ((F_CPU / 18) < desiredclk) {
|
||||
#ifdef DEBUG_SERIAL
|
||||
Serial.println(F("I2C.setSpeed too high."));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
uint32_t atwbr = ((F_CPU / desiredclk) - 16) / 2;
|
||||
if (atwbr > 16320) {
|
||||
#ifdef DEBUG_SERIAL
|
||||
Serial.println(F("I2C.setSpeed too low."));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (atwbr <= 255) {
|
||||
atwbr /= 1;
|
||||
TWSR = 0x0;
|
||||
} else if (atwbr <= 1020) {
|
||||
atwbr /= 4;
|
||||
TWSR = 0x1;
|
||||
} else if (atwbr <= 4080) {
|
||||
atwbr /= 16;
|
||||
TWSR = 0x2;
|
||||
} else { // if (atwbr <= 16320)
|
||||
atwbr /= 64;
|
||||
TWSR = 0x3;
|
||||
}
|
||||
TWBR = atwbr;
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
Serial.print(F("TWSR prescaler = "));
|
||||
Serial.println(pow(4, TWSR));
|
||||
Serial.print(F("TWBR = "));
|
||||
Serial.println(atwbr);
|
||||
#endif
|
||||
return true;
|
||||
#elif (ARDUINO >= 157) && !defined(ARDUINO_STM32_FEATHER) && \
|
||||
!defined(TinyWireM_h)
|
||||
_wire->setClock(desiredclk);
|
||||
return true;
|
||||
|
||||
#else
|
||||
(void)desiredclk;
|
||||
return false;
|
||||
+1
-1
@@ -15,7 +15,7 @@ public:
|
||||
|
||||
bool read(uint8_t *buffer, size_t len, bool stop = true);
|
||||
bool write(const uint8_t *buffer, size_t len, bool stop = true,
|
||||
const uint8_t *prefix_buffer = NULL, size_t prefix_len = 0);
|
||||
const uint8_t *prefix_buffer = nullptr, size_t prefix_len = 0);
|
||||
bool write_then_read(const uint8_t *write_buffer, size_t write_len,
|
||||
uint8_t *read_buffer, size_t read_len,
|
||||
bool stop = false);
|
||||
+139
-126
@@ -1,9 +1,24 @@
|
||||
#include "Adafruit_SPIDevice.h"
|
||||
|
||||
#if !defined(SPI_INTERFACES_COUNT) || \
|
||||
(defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0))
|
||||
// #define DEBUG_SERIAL Serial
|
||||
|
||||
//#define DEBUG_SERIAL Serial
|
||||
#ifdef BUSIO_USE_FAST_PINIO
|
||||
#define BUSIO_SET_CLOCK_LOW() (*clkPort = *clkPort & ~clkPinMask)
|
||||
#define BUSIO_SET_CLOCK_HIGH() (*clkPort = *clkPort | clkPinMask)
|
||||
#define BUSIO_READ_MISO() (*misoPort & misoPinMask)
|
||||
#define BUSIO_WRITE_MOSI(value) \
|
||||
do { \
|
||||
if (value) \
|
||||
*mosiPort = *mosiPort | mosiPinMask; \
|
||||
else \
|
||||
*mosiPort = *mosiPort & ~mosiPinMask; \
|
||||
} while (0)
|
||||
#else
|
||||
#define BUSIO_SET_CLOCK_LOW() digitalWrite(_sck, LOW)
|
||||
#define BUSIO_SET_CLOCK_HIGH() digitalWrite(_sck, HIGH)
|
||||
#define BUSIO_READ_MISO() digitalRead(_miso)
|
||||
#define BUSIO_WRITE_MOSI(value) digitalWrite(_mosi, value)
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* @brief Create an SPI device with the given CS pin and settings
|
||||
@@ -17,6 +32,7 @@
|
||||
Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, uint32_t freq,
|
||||
BusIOBitOrder dataOrder,
|
||||
uint8_t dataMode, SPIClass *theSPI) {
|
||||
#ifdef BUSIO_HAS_HW_SPI
|
||||
_cs = cspin;
|
||||
_sck = _mosi = _miso = -1;
|
||||
_spi = theSPI;
|
||||
@@ -25,6 +41,14 @@ Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, uint32_t freq,
|
||||
_freq = freq;
|
||||
_dataOrder = dataOrder;
|
||||
_dataMode = dataMode;
|
||||
#else
|
||||
// unused, but needed to suppress compiler warns
|
||||
(void)cspin;
|
||||
(void)freq;
|
||||
(void)dataOrder;
|
||||
(void)dataMode;
|
||||
(void)theSPI;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -68,18 +92,14 @@ Adafruit_SPIDevice::Adafruit_SPIDevice(int8_t cspin, int8_t sckpin,
|
||||
_dataOrder = dataOrder;
|
||||
_dataMode = dataMode;
|
||||
_begun = false;
|
||||
_spiSetting = new SPISettings(freq, dataOrder, dataMode);
|
||||
_spi = NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Release memory allocated in constructors
|
||||
*/
|
||||
Adafruit_SPIDevice::~Adafruit_SPIDevice() {
|
||||
if (_spiSetting) {
|
||||
if (_spiSetting)
|
||||
delete _spiSetting;
|
||||
_spiSetting = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -94,7 +114,9 @@ bool Adafruit_SPIDevice::begin(void) {
|
||||
}
|
||||
|
||||
if (_spi) { // hardware SPI
|
||||
#ifdef BUSIO_HAS_HW_SPI
|
||||
_spi->begin();
|
||||
#endif
|
||||
} else {
|
||||
pinMode(_sck, OUTPUT);
|
||||
|
||||
@@ -119,16 +141,19 @@ bool Adafruit_SPIDevice::begin(void) {
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Transfer (send/receive) one byte over hard/soft SPI
|
||||
* @brief Transfer (send/receive) a buffer over hard/soft SPI, without
|
||||
* transaction management
|
||||
* @param buffer The buffer to send and receive at the same time
|
||||
* @param len The number of bytes to transfer
|
||||
*/
|
||||
void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) {
|
||||
//
|
||||
// HARDWARE SPI
|
||||
//
|
||||
if (_spi) {
|
||||
// hardware SPI is easy
|
||||
|
||||
#ifdef BUSIO_HAS_HW_SPI
|
||||
#if defined(SPARK)
|
||||
_spi->transfer(buffer, buffer, len, NULL);
|
||||
_spi->transfer(buffer, buffer, len, nullptr);
|
||||
#elif defined(STM32)
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
_spi->transfer(buffer[i]);
|
||||
@@ -137,8 +162,12 @@ void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) {
|
||||
_spi->transfer(buffer, len);
|
||||
#endif
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// SOFTWARE SPI
|
||||
//
|
||||
uint8_t startbit;
|
||||
if (_dataOrder == SPI_BITORDER_LSBFIRST) {
|
||||
startbit = 0x1;
|
||||
@@ -149,9 +178,7 @@ void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) {
|
||||
bool towrite, lastmosi = !(buffer[0] & startbit);
|
||||
uint8_t bitdelay_us = (1000000 / _freq) / 2;
|
||||
|
||||
// for softSPI we'll do it by hand
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
// software SPI
|
||||
uint8_t reply = 0;
|
||||
uint8_t send = buffer[i];
|
||||
|
||||
@@ -172,91 +199,78 @@ void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) {
|
||||
if (_dataMode == SPI_MODE0 || _dataMode == SPI_MODE2) {
|
||||
towrite = send & b;
|
||||
if ((_mosi != -1) && (lastmosi != towrite)) {
|
||||
#ifdef BUSIO_USE_FAST_PINIO
|
||||
if (towrite)
|
||||
*mosiPort |= mosiPinMask;
|
||||
else
|
||||
*mosiPort &= ~mosiPinMask;
|
||||
#else
|
||||
digitalWrite(_mosi, towrite);
|
||||
#endif
|
||||
BUSIO_WRITE_MOSI(towrite);
|
||||
lastmosi = towrite;
|
||||
}
|
||||
|
||||
#ifdef BUSIO_USE_FAST_PINIO
|
||||
*clkPort |= clkPinMask; // Clock high
|
||||
#else
|
||||
digitalWrite(_sck, HIGH);
|
||||
#endif
|
||||
BUSIO_SET_CLOCK_HIGH();
|
||||
|
||||
if (bitdelay_us) {
|
||||
delayMicroseconds(bitdelay_us);
|
||||
}
|
||||
|
||||
if (_miso != -1) {
|
||||
#ifdef BUSIO_USE_FAST_PINIO
|
||||
if (*misoPort & misoPinMask) {
|
||||
#else
|
||||
if (digitalRead(_miso)) {
|
||||
#endif
|
||||
if (BUSIO_READ_MISO())
|
||||
reply |= b;
|
||||
}
|
||||
|
||||
BUSIO_SET_CLOCK_LOW();
|
||||
|
||||
} else if (_dataMode == SPI_MODE3) {
|
||||
|
||||
if (_mosi != -1) { // transmit on falling edge
|
||||
BUSIO_WRITE_MOSI(send & b);
|
||||
}
|
||||
|
||||
BUSIO_SET_CLOCK_LOW();
|
||||
|
||||
if (bitdelay_us) {
|
||||
delayMicroseconds(bitdelay_us);
|
||||
}
|
||||
|
||||
BUSIO_SET_CLOCK_HIGH();
|
||||
|
||||
if (bitdelay_us) {
|
||||
delayMicroseconds(bitdelay_us);
|
||||
}
|
||||
|
||||
if (_miso != -1) { // read on rising edge
|
||||
if (BUSIO_READ_MISO()) {
|
||||
reply |= b;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BUSIO_USE_FAST_PINIO
|
||||
*clkPort &= ~clkPinMask; // Clock low
|
||||
#else
|
||||
digitalWrite(_sck, LOW);
|
||||
#endif
|
||||
} else { // if (_dataMode == SPI_MODE1 || _dataMode == SPI_MODE3)
|
||||
} else { // || _dataMode == SPI_MODE1)
|
||||
|
||||
#ifdef BUSIO_USE_FAST_PINIO
|
||||
*clkPort |= clkPinMask; // Clock high
|
||||
#else
|
||||
digitalWrite(_sck, HIGH);
|
||||
#endif
|
||||
BUSIO_SET_CLOCK_HIGH();
|
||||
|
||||
if (bitdelay_us) {
|
||||
delayMicroseconds(bitdelay_us);
|
||||
}
|
||||
|
||||
if (_mosi != -1) {
|
||||
#ifdef BUSIO_USE_FAST_PINIO
|
||||
if (send & b)
|
||||
*mosiPort |= mosiPinMask;
|
||||
else
|
||||
*mosiPort &= ~mosiPinMask;
|
||||
#else
|
||||
digitalWrite(_mosi, send & b);
|
||||
#endif
|
||||
BUSIO_WRITE_MOSI(send & b);
|
||||
}
|
||||
|
||||
#ifdef BUSIO_USE_FAST_PINIO
|
||||
*clkPort &= ~clkPinMask; // Clock low
|
||||
#else
|
||||
digitalWrite(_sck, LOW);
|
||||
#endif
|
||||
BUSIO_SET_CLOCK_LOW();
|
||||
|
||||
if (_miso != -1) {
|
||||
#ifdef BUSIO_USE_FAST_PINIO
|
||||
if (*misoPort & misoPinMask) {
|
||||
#else
|
||||
if (digitalRead(_miso)) {
|
||||
#endif
|
||||
if (BUSIO_READ_MISO()) {
|
||||
reply |= b;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_miso != -1) {
|
||||
buffer[i] = reply;
|
||||
}
|
||||
}
|
||||
if (_miso != -1) {
|
||||
buffer[i] = reply;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Transfer (send/receive) one byte over hard/soft SPI
|
||||
* @brief Transfer (send/receive) one byte over hard/soft SPI, without
|
||||
* transaction management
|
||||
* @param send The byte to send
|
||||
* @return The byte received while transmitting
|
||||
*/
|
||||
@@ -272,7 +286,9 @@ uint8_t Adafruit_SPIDevice::transfer(uint8_t send) {
|
||||
*/
|
||||
void Adafruit_SPIDevice::beginTransaction(void) {
|
||||
if (_spi) {
|
||||
#ifdef BUSIO_HAS_HW_SPI
|
||||
_spi->beginTransaction(*_spiSetting);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,12 +297,45 @@ void Adafruit_SPIDevice::beginTransaction(void) {
|
||||
*/
|
||||
void Adafruit_SPIDevice::endTransaction(void) {
|
||||
if (_spi) {
|
||||
#ifdef BUSIO_HAS_HW_SPI
|
||||
_spi->endTransaction();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Write a buffer or two to the SPI device.
|
||||
* @brief Assert/Deassert the CS pin if it is defined
|
||||
* @param value The state the CS is set to
|
||||
*/
|
||||
void Adafruit_SPIDevice::setChipSelect(int value) {
|
||||
if (_cs != -1) {
|
||||
digitalWrite(_cs, value);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Write a buffer or two to the SPI device, with transaction
|
||||
* management.
|
||||
* @brief Manually begin a transaction (calls beginTransaction if hardware
|
||||
* SPI) with asserting the CS pin
|
||||
*/
|
||||
void Adafruit_SPIDevice::beginTransactionWithAssertingCS() {
|
||||
beginTransaction();
|
||||
setChipSelect(LOW);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Manually end a transaction (calls endTransaction if hardware SPI)
|
||||
* with deasserting the CS pin
|
||||
*/
|
||||
void Adafruit_SPIDevice::endTransactionWithDeassertingCS() {
|
||||
setChipSelect(HIGH);
|
||||
endTransaction();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Write a buffer or two to the SPI device, with transaction
|
||||
* management.
|
||||
* @param buffer Pointer to buffer of data to write
|
||||
* @param len Number of bytes from buffer to write
|
||||
* @param prefix_buffer Pointer to optional array of data to write before
|
||||
@@ -295,21 +344,19 @@ void Adafruit_SPIDevice::endTransaction(void) {
|
||||
* @return Always returns true because there's no way to test success of SPI
|
||||
* writes
|
||||
*/
|
||||
bool Adafruit_SPIDevice::write(uint8_t *buffer, size_t len,
|
||||
uint8_t *prefix_buffer, size_t prefix_len) {
|
||||
if (_spi) {
|
||||
_spi->beginTransaction(*_spiSetting);
|
||||
}
|
||||
bool Adafruit_SPIDevice::write(const uint8_t *buffer, size_t len,
|
||||
const uint8_t *prefix_buffer,
|
||||
size_t prefix_len) {
|
||||
beginTransactionWithAssertingCS();
|
||||
|
||||
setChipSelect(LOW);
|
||||
// do the writing
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
if (_spi) {
|
||||
if (prefix_len > 0) {
|
||||
_spi->transferBytes(prefix_buffer, nullptr, prefix_len);
|
||||
_spi->transferBytes((uint8_t *)prefix_buffer, nullptr, prefix_len);
|
||||
}
|
||||
if (len > 0) {
|
||||
_spi->transferBytes(buffer, nullptr, len);
|
||||
_spi->transferBytes((uint8_t *)buffer, nullptr, len);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
@@ -321,15 +368,11 @@ bool Adafruit_SPIDevice::write(uint8_t *buffer, size_t len,
|
||||
transfer(buffer[i]);
|
||||
}
|
||||
}
|
||||
setChipSelect(HIGH);
|
||||
|
||||
if (_spi) {
|
||||
_spi->endTransaction();
|
||||
}
|
||||
endTransactionWithDeassertingCS();
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: "));
|
||||
if ((prefix_len != 0) && (prefix_buffer != NULL)) {
|
||||
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
|
||||
for (uint16_t i = 0; i < prefix_len; i++) {
|
||||
DEBUG_SERIAL.print(F("0x"));
|
||||
DEBUG_SERIAL.print(prefix_buffer[i], HEX);
|
||||
@@ -351,7 +394,8 @@ bool Adafruit_SPIDevice::write(uint8_t *buffer, size_t len,
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Read from SPI into a buffer from the SPI device.
|
||||
* @brief Read from SPI into a buffer from the SPI device, with transaction
|
||||
* management.
|
||||
* @param buffer Pointer to buffer of data to read into
|
||||
* @param len Number of bytes from buffer to read.
|
||||
* @param sendvalue The 8-bits of data to write when doing the data read,
|
||||
@@ -361,17 +405,10 @@ bool Adafruit_SPIDevice::write(uint8_t *buffer, size_t len,
|
||||
*/
|
||||
bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
|
||||
memset(buffer, sendvalue, len); // clear out existing buffer
|
||||
if (_spi) {
|
||||
_spi->beginTransaction(*_spiSetting);
|
||||
}
|
||||
|
||||
setChipSelect(LOW);
|
||||
beginTransactionWithAssertingCS();
|
||||
transfer(buffer, len);
|
||||
setChipSelect(HIGH);
|
||||
|
||||
if (_spi) {
|
||||
_spi->endTransaction();
|
||||
}
|
||||
endTransactionWithDeassertingCS();
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
DEBUG_SERIAL.print(F("\tSPIDevice Read: "));
|
||||
@@ -390,9 +427,9 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Write some data, then read some data from SPI into another buffer.
|
||||
* The buffers can point to same/overlapping locations. This does not
|
||||
* transmit-receive at the same time!
|
||||
* @brief Write some data, then read some data from SPI into another buffer,
|
||||
* with transaction management. The buffers can point to same/overlapping
|
||||
* locations. This does not transmit-receive at the same time!
|
||||
* @param write_buffer Pointer to buffer of data to write from
|
||||
* @param write_len Number of bytes from buffer to write.
|
||||
* @param read_buffer Pointer to buffer of data to read into.
|
||||
@@ -402,19 +439,15 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
|
||||
* @return Always returns true because there's no way to test success of SPI
|
||||
* writes
|
||||
*/
|
||||
bool Adafruit_SPIDevice::write_then_read(uint8_t *write_buffer,
|
||||
bool Adafruit_SPIDevice::write_then_read(const uint8_t *write_buffer,
|
||||
size_t write_len, uint8_t *read_buffer,
|
||||
size_t read_len, uint8_t sendvalue) {
|
||||
if (_spi) {
|
||||
_spi->beginTransaction(*_spiSetting);
|
||||
}
|
||||
|
||||
setChipSelect(LOW);
|
||||
beginTransactionWithAssertingCS();
|
||||
// do the writing
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
if (_spi) {
|
||||
if (write_len > 0) {
|
||||
_spi->transferBytes(write_buffer, nullptr, write_len);
|
||||
_spi->transferBytes((uint8_t *)write_buffer, nullptr, write_len);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
@@ -455,45 +488,25 @@ bool Adafruit_SPIDevice::write_then_read(uint8_t *write_buffer,
|
||||
DEBUG_SERIAL.println();
|
||||
#endif
|
||||
|
||||
setChipSelect(HIGH);
|
||||
|
||||
if (_spi) {
|
||||
_spi->endTransaction();
|
||||
}
|
||||
endTransactionWithDeassertingCS();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Write some data and read some data at the same time from SPI
|
||||
* into the same buffer. This is basicaly a wrapper for transfer() with
|
||||
* CS-pin and transaction management.
|
||||
* This /does/ transmit-receive at the same time!
|
||||
* into the same buffer, with transaction management. This is basicaly a wrapper
|
||||
* for transfer() with CS-pin and transaction management. This /does/
|
||||
* transmit-receive at the same time!
|
||||
* @param buffer Pointer to buffer of data to write/read to/from
|
||||
* @param len Number of bytes from buffer to write/read.
|
||||
* @return Always returns true because there's no way to test success of SPI
|
||||
* writes
|
||||
*/
|
||||
bool Adafruit_SPIDevice::write_and_read(uint8_t *buffer, size_t len) {
|
||||
if (_spi) {
|
||||
_spi->beginTransaction(*_spiSetting);
|
||||
}
|
||||
|
||||
setChipSelect(LOW);
|
||||
beginTransactionWithAssertingCS();
|
||||
transfer(buffer, len);
|
||||
setChipSelect(HIGH);
|
||||
|
||||
if (_spi) {
|
||||
_spi->endTransaction();
|
||||
}
|
||||
endTransactionWithDeassertingCS();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Adafruit_SPIDevice::setChipSelect(int value) {
|
||||
if (_cs == -1)
|
||||
return;
|
||||
digitalWrite(_cs, value);
|
||||
}
|
||||
|
||||
#endif // SPI exists
|
||||
+51
-11
@@ -5,8 +5,14 @@
|
||||
|
||||
#if !defined(SPI_INTERFACES_COUNT) || \
|
||||
(defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0))
|
||||
|
||||
// HW SPI available
|
||||
#include <SPI.h>
|
||||
#define BUSIO_HAS_HW_SPI
|
||||
#else
|
||||
// SW SPI ONLY
|
||||
enum { SPI_MODE0, SPI_MODE1, SPI_MODE2, SPI_MODE3 };
|
||||
typedef uint8_t SPIClass;
|
||||
#endif
|
||||
|
||||
// some modern SPI definitions don't have BitOrder enum
|
||||
#if (defined(__AVR__) && !defined(ARDUINO_ARCH_MEGAAVR)) || \
|
||||
@@ -16,7 +22,8 @@
|
||||
defined(ARDUINO_AVR_ATmega4808) || defined(ARDUINO_AVR_ATmega3209) || \
|
||||
defined(ARDUINO_AVR_ATmega3208) || defined(ARDUINO_AVR_ATmega1609) || \
|
||||
defined(ARDUINO_AVR_ATmega1608) || defined(ARDUINO_AVR_ATmega809) || \
|
||||
defined(ARDUINO_AVR_ATmega808) || defined(ARDUINO_ARCH_ARC32)
|
||||
defined(ARDUINO_AVR_ATmega808) || defined(ARDUINO_ARCH_ARC32) || \
|
||||
defined(ARDUINO_ARCH_XMC)
|
||||
|
||||
typedef enum _BitOrder {
|
||||
SPI_BITORDER_MSBFIRST = MSBFIRST,
|
||||
@@ -39,7 +46,27 @@ typedef enum _BitOrder {
|
||||
typedef BitOrder BusIOBitOrder;
|
||||
#endif
|
||||
|
||||
#if defined(__AVR__) || defined(TEENSYDUINO)
|
||||
#if defined(__IMXRT1062__) // Teensy 4.x
|
||||
// *Warning* I disabled the usage of FAST_PINIO as the set/clear operations
|
||||
// used in the cpp file are not atomic and can effect multiple IO pins
|
||||
// and if an interrupt happens in between the time the code reads the register
|
||||
// and writes out the updated value, that changes one or more other IO pins
|
||||
// on that same IO port, those change will be clobbered when the updated
|
||||
// values are written back. A fast version can be implemented that uses the
|
||||
// ports set and clear registers which are atomic.
|
||||
// typedef volatile uint32_t BusIO_PortReg;
|
||||
// typedef uint32_t BusIO_PortMask;
|
||||
// #define BUSIO_USE_FAST_PINIO
|
||||
|
||||
#elif defined(__MBED__) || defined(__ZEPHYR__)
|
||||
// Boards based on RTOS cores like mbed or Zephyr are not going to expose the
|
||||
// low level registers needed for fast pin manipulation
|
||||
#undef BUSIO_USE_FAST_PINIO
|
||||
|
||||
#elif defined(ARDUINO_ARCH_XMC)
|
||||
#undef BUSIO_USE_FAST_PINIO
|
||||
|
||||
#elif defined(__AVR__) || defined(TEENSYDUINO)
|
||||
typedef volatile uint8_t BusIO_PortReg;
|
||||
typedef uint8_t BusIO_PortMask;
|
||||
#define BUSIO_USE_FAST_PINIO
|
||||
@@ -51,7 +78,9 @@ typedef uint32_t BusIO_PortMask;
|
||||
#define BUSIO_USE_FAST_PINIO
|
||||
|
||||
#elif (defined(__arm__) || defined(ARDUINO_FEATHER52)) && \
|
||||
!defined(ARDUINO_ARCH_MBED) && !defined(ARDUINO_ARCH_RP2040)
|
||||
!defined(ARDUINO_ARCH_RP2040) && !defined(ARDUINO_SILABS) && \
|
||||
!defined(ARDUINO_UNOR4_MINIMA) && !defined(ARDUINO_UNOR4_WIFI) && \
|
||||
!defined(PORTDUINO)
|
||||
typedef volatile uint32_t BusIO_PortReg;
|
||||
typedef uint32_t BusIO_PortMask;
|
||||
#if !defined(__ASR6501__) && !defined(__ASR6502__)
|
||||
@@ -65,10 +94,15 @@ typedef uint32_t BusIO_PortMask;
|
||||
/**! The class which defines how we will talk to this device over SPI **/
|
||||
class Adafruit_SPIDevice {
|
||||
public:
|
||||
#ifdef BUSIO_HAS_HW_SPI
|
||||
Adafruit_SPIDevice(int8_t cspin, uint32_t freq = 1000000,
|
||||
BusIOBitOrder dataOrder = SPI_BITORDER_MSBFIRST,
|
||||
uint8_t dataMode = SPI_MODE0, SPIClass *theSPI = &SPI);
|
||||
|
||||
#else
|
||||
Adafruit_SPIDevice(int8_t cspin, uint32_t freq = 1000000,
|
||||
BusIOBitOrder dataOrder = SPI_BITORDER_MSBFIRST,
|
||||
uint8_t dataMode = SPI_MODE0, SPIClass *theSPI = nullptr);
|
||||
#endif
|
||||
Adafruit_SPIDevice(int8_t cspin, int8_t sck, int8_t miso, int8_t mosi,
|
||||
uint32_t freq = 1000000,
|
||||
BusIOBitOrder dataOrder = SPI_BITORDER_MSBFIRST,
|
||||
@@ -77,9 +111,9 @@ public:
|
||||
|
||||
bool begin(void);
|
||||
bool read(uint8_t *buffer, size_t len, uint8_t sendvalue = 0xFF);
|
||||
bool write(uint8_t *buffer, size_t len, uint8_t *prefix_buffer = NULL,
|
||||
size_t prefix_len = 0);
|
||||
bool write_then_read(uint8_t *write_buffer, size_t write_len,
|
||||
bool write(const uint8_t *buffer, size_t len,
|
||||
const uint8_t *prefix_buffer = nullptr, size_t prefix_len = 0);
|
||||
bool write_then_read(const uint8_t *write_buffer, size_t write_len,
|
||||
uint8_t *read_buffer, size_t read_len,
|
||||
uint8_t sendvalue = 0xFF);
|
||||
bool write_and_read(uint8_t *buffer, size_t len);
|
||||
@@ -88,10 +122,17 @@ public:
|
||||
void transfer(uint8_t *buffer, size_t len);
|
||||
void beginTransaction(void);
|
||||
void endTransaction(void);
|
||||
void beginTransactionWithAssertingCS();
|
||||
void endTransactionWithDeassertingCS();
|
||||
|
||||
private:
|
||||
SPIClass *_spi;
|
||||
SPISettings *_spiSetting;
|
||||
#ifdef BUSIO_HAS_HW_SPI
|
||||
SPIClass *_spi = nullptr;
|
||||
SPISettings *_spiSetting = nullptr;
|
||||
#else
|
||||
uint8_t *_spi = nullptr;
|
||||
uint8_t *_spiSetting = nullptr;
|
||||
#endif
|
||||
uint32_t _freq;
|
||||
BusIOBitOrder _dataOrder;
|
||||
uint8_t _dataMode;
|
||||
@@ -105,5 +146,4 @@ private:
|
||||
bool _begun;
|
||||
};
|
||||
|
||||
#endif // has SPI defined
|
||||
#endif // Adafruit_SPIDevice_h
|
||||
@@ -1,7 +1,7 @@
|
||||
# Adafruit Bus IO Library [](https://github.com/adafruit/Adafruit_BusIO/actions)
|
||||
|
||||
|
||||
This is a helper library to abstract away I2C & SPI transactions and registers
|
||||
This is a helper library to abstract away I2C, SPI, and 'generic transport' (e.g. UART) transactions and registers
|
||||
|
||||
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS = .
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
Advanced example of using bstracted transport for reading and writing
|
||||
register data from a UART-based device such as a TMC2209
|
||||
|
||||
Written with help by Claude!
|
||||
https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c (at this time
|
||||
chats are not shareable :(
|
||||
*/
|
||||
|
||||
#include "Adafruit_BusIO_Register.h"
|
||||
#include "Adafruit_GenericDevice.h"
|
||||
|
||||
// Debugging macros
|
||||
#define DEBUG_SERIAL Serial
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
#define DEBUG_PRINT(x) DEBUG_SERIAL.print(x)
|
||||
#define DEBUG_PRINTLN(x) DEBUG_SERIAL.println(x)
|
||||
#define DEBUG_PRINT_HEX(x) \
|
||||
do { \
|
||||
if (x < 0x10) \
|
||||
DEBUG_SERIAL.print('0'); \
|
||||
DEBUG_SERIAL.print(x, HEX); \
|
||||
DEBUG_SERIAL.print(' '); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define DEBUG_PRINT_HEX(x)
|
||||
#endif
|
||||
|
||||
#define TMC2209_IOIN 0x06
|
||||
|
||||
class TMC2209_UART {
|
||||
private:
|
||||
Stream *_uart_stream;
|
||||
uint8_t _addr;
|
||||
|
||||
static bool uart_read(void *thiz, uint8_t *buffer, size_t len) {
|
||||
TMC2209_UART *dev = (TMC2209_UART *)thiz;
|
||||
uint16_t timeout = 100;
|
||||
while (dev->_uart_stream->available() < len && timeout--) {
|
||||
delay(1);
|
||||
}
|
||||
if (timeout == 0) {
|
||||
DEBUG_PRINTLN("Read timeout!");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_PRINT("Reading: ");
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
buffer[i] = dev->_uart_stream->read();
|
||||
DEBUG_PRINT_HEX(buffer[i]);
|
||||
}
|
||||
DEBUG_PRINTLN("");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool uart_write(void *thiz, const uint8_t *buffer, size_t len) {
|
||||
TMC2209_UART *dev = (TMC2209_UART *)thiz;
|
||||
DEBUG_PRINT("Writing: ");
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
DEBUG_PRINT_HEX(buffer[i]);
|
||||
}
|
||||
DEBUG_PRINTLN("");
|
||||
|
||||
dev->_uart_stream->write(buffer, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool uart_readreg(void *thiz, uint8_t *addr_buf, uint8_t addrsiz,
|
||||
uint8_t *data, uint16_t datalen) {
|
||||
TMC2209_UART *dev = (TMC2209_UART *)thiz;
|
||||
while (dev->_uart_stream->available())
|
||||
dev->_uart_stream->read();
|
||||
|
||||
uint8_t packet[4] = {0x05, uint8_t(dev->_addr << 1), addr_buf[0], 0x00};
|
||||
|
||||
packet[3] = calcCRC(packet, 3);
|
||||
if (!uart_write(thiz, packet, 4))
|
||||
return false;
|
||||
|
||||
// Read back echo
|
||||
uint8_t echo[4];
|
||||
if (!uart_read(thiz, echo, 4))
|
||||
return false;
|
||||
|
||||
// Verify echo
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (echo[i] != packet[i]) {
|
||||
DEBUG_PRINTLN("Echo mismatch");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t response[8]; // sync + 0xFF + reg + 4 data bytes + CRC
|
||||
if (!uart_read(thiz, response, 8))
|
||||
return false;
|
||||
|
||||
// Verify response
|
||||
if (response[0] != 0x05) {
|
||||
DEBUG_PRINTLN("Invalid sync byte");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response[1] != 0xFF) {
|
||||
DEBUG_PRINTLN("Invalid reply address");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response[2] != addr_buf[0]) {
|
||||
DEBUG_PRINTLN("Register mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t crc = calcCRC(response, 7);
|
||||
if (crc != response[7]) {
|
||||
DEBUG_PRINTLN("CRC mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data, &response[3], 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool uart_writereg(void *thiz, uint8_t *addr_buf, uint8_t addrsiz,
|
||||
const uint8_t *data, uint16_t datalen) {
|
||||
TMC2209_UART *dev = (TMC2209_UART *)thiz;
|
||||
while (dev->_uart_stream->available())
|
||||
dev->_uart_stream->read();
|
||||
|
||||
uint8_t packet[8] = {0x05,
|
||||
uint8_t(dev->_addr << 1),
|
||||
uint8_t(addr_buf[0] | 0x80),
|
||||
data[0],
|
||||
data[1],
|
||||
data[2],
|
||||
data[3],
|
||||
0x00};
|
||||
|
||||
packet[7] = calcCRC(packet, 7);
|
||||
if (!uart_write(thiz, packet, 8))
|
||||
return false;
|
||||
|
||||
uint8_t echo[8];
|
||||
if (!uart_read(thiz, echo, 8))
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (echo[i] != packet[i]) {
|
||||
DEBUG_PRINTLN("Write echo mismatch");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t calcCRC(uint8_t *data, uint8_t length) {
|
||||
uint8_t crc = 0;
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
uint8_t currentByte = data[i];
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
if ((crc >> 7) ^ (currentByte & 0x01)) {
|
||||
crc = (crc << 1) ^ 0x07;
|
||||
} else {
|
||||
crc = crc << 1;
|
||||
}
|
||||
currentByte = currentByte >> 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
public:
|
||||
TMC2209_UART(Stream *serial, uint8_t addr)
|
||||
: _uart_stream(serial), _addr(addr) {}
|
||||
|
||||
Adafruit_GenericDevice *createDevice() {
|
||||
return new Adafruit_GenericDevice(this, uart_read, uart_write, uart_readreg,
|
||||
uart_writereg);
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial)
|
||||
;
|
||||
delay(100);
|
||||
Serial.println("TMC2209 Generic Device register read/write test!");
|
||||
|
||||
Serial1.begin(115200);
|
||||
|
||||
TMC2209_UART uart(&Serial1, 0);
|
||||
Adafruit_GenericDevice *device = uart.createDevice();
|
||||
device->begin();
|
||||
|
||||
// Create register object for IOIN
|
||||
Adafruit_BusIO_Register ioin_reg(device,
|
||||
TMC2209_IOIN, // device and register address
|
||||
4, // width = 4 bytes
|
||||
MSBFIRST, // byte order
|
||||
1); // address width = 1 byte
|
||||
Serial.print("IOIN = 0x");
|
||||
Serial.println(ioin_reg.read(), HEX);
|
||||
|
||||
// Create RegisterBits for VERSION field (bits 31:24)
|
||||
Adafruit_BusIO_RegisterBits version_bits(
|
||||
&ioin_reg, 8, 24); // 8 bits wide, starting at bit 24
|
||||
|
||||
Serial.println("Reading VERSION...");
|
||||
uint8_t version = version_bits.read();
|
||||
|
||||
Serial.print("VERSION = 0x");
|
||||
Serial.println(version, HEX);
|
||||
}
|
||||
|
||||
void loop() { delay(1000); }
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Abstracted transport for reading and writing data from a UART-based
|
||||
device such as a TMC2209
|
||||
|
||||
Written with help by Claude!
|
||||
https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c (at this time
|
||||
chats are not shareable :(
|
||||
*/
|
||||
|
||||
#include "Adafruit_GenericDevice.h"
|
||||
|
||||
/**
|
||||
* Basic UART device class that demonstrates using GenericDevice with a Stream
|
||||
* interface. This example shows how to wrap a Stream (like HardwareSerial or
|
||||
* SoftwareSerial) with read/write callbacks that can be used by BusIO's
|
||||
* register functions.
|
||||
*/
|
||||
class UARTDevice {
|
||||
public:
|
||||
UARTDevice(Stream *serial) : _serial(serial) {}
|
||||
|
||||
// Static callback for writing data to UART
|
||||
// Called by GenericDevice when data needs to be sent
|
||||
static bool uart_write(void *thiz, const uint8_t *buffer, size_t len) {
|
||||
UARTDevice *dev = (UARTDevice *)thiz;
|
||||
dev->_serial->write(buffer, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Static callback for reading data from UART
|
||||
// Includes timeout and will return false if not enough data available
|
||||
static bool uart_read(void *thiz, uint8_t *buffer, size_t len) {
|
||||
UARTDevice *dev = (UARTDevice *)thiz;
|
||||
uint16_t timeout = 100;
|
||||
while (dev->_serial->available() < len && timeout--) {
|
||||
delay(1);
|
||||
}
|
||||
if (timeout == 0) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
buffer[i] = dev->_serial->read();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a GenericDevice instance using our callbacks
|
||||
Adafruit_GenericDevice *createDevice() {
|
||||
return new Adafruit_GenericDevice(this, uart_read, uart_write);
|
||||
}
|
||||
|
||||
private:
|
||||
Stream *_serial; // Underlying Stream instance (HardwareSerial, etc)
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial)
|
||||
;
|
||||
delay(100);
|
||||
|
||||
Serial.println("Generic Device test!");
|
||||
|
||||
// Initialize UART for device communication
|
||||
Serial1.begin(115200);
|
||||
|
||||
// Create UART wrapper and BusIO device
|
||||
UARTDevice uart(&Serial1);
|
||||
Adafruit_GenericDevice *device = uart.createDevice();
|
||||
device->begin();
|
||||
|
||||
// Test write/read cycle
|
||||
uint8_t write_buf[4] = {0x5, 0x0, 0x0, 0x48};
|
||||
uint8_t read_buf[8];
|
||||
|
||||
Serial.println("Writing data...");
|
||||
if (!device->write(write_buf, 4)) {
|
||||
Serial.println("Write failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("Reading response...");
|
||||
if (!device->read(read_buf, 8)) {
|
||||
Serial.println("Read failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Print response bytes
|
||||
Serial.print("Got response: ");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Serial.print("0x");
|
||||
Serial.print(read_buf[i], HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void loop() { delay(1000); }
|
||||
+6
-5
@@ -3,19 +3,20 @@
|
||||
Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(0x10);
|
||||
|
||||
void setup() {
|
||||
while (!Serial) { delay(10); }
|
||||
while (!Serial) {
|
||||
delay(10);
|
||||
}
|
||||
Serial.begin(115200);
|
||||
Serial.println("I2C address detection test");
|
||||
|
||||
if (!i2c_dev.begin()) {
|
||||
Serial.print("Did not find device at 0x");
|
||||
Serial.println(i2c_dev.address(), HEX);
|
||||
while (1);
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
Serial.print("Device found on address 0x");
|
||||
Serial.println(i2c_dev.address(), HEX);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
||||
void loop() {}
|
||||
+15
-11
@@ -3,16 +3,18 @@
|
||||
#define I2C_ADDRESS 0x60
|
||||
Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(I2C_ADDRESS);
|
||||
|
||||
|
||||
void setup() {
|
||||
while (!Serial) { delay(10); }
|
||||
while (!Serial) {
|
||||
delay(10);
|
||||
}
|
||||
Serial.begin(115200);
|
||||
Serial.println("I2C device read and write test");
|
||||
|
||||
if (!i2c_dev.begin()) {
|
||||
Serial.print("Did not find device at 0x");
|
||||
Serial.println(i2c_dev.address(), HEX);
|
||||
while (1);
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
Serial.print("Device found on address 0x");
|
||||
Serial.println(i2c_dev.address(), HEX);
|
||||
@@ -21,21 +23,23 @@ void setup() {
|
||||
// Try to read 32 bytes
|
||||
i2c_dev.read(buffer, 32);
|
||||
Serial.print("Read: ");
|
||||
for (uint8_t i=0; i<32; i++) {
|
||||
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
Serial.print("0x");
|
||||
Serial.print(buffer[i], HEX);
|
||||
Serial.print(", ");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// read a register by writing first, then reading
|
||||
buffer[0] = 0x0C; // we'll reuse the same buffer
|
||||
buffer[0] = 0x0C; // we'll reuse the same buffer
|
||||
i2c_dev.write_then_read(buffer, 1, buffer, 2, false);
|
||||
Serial.print("Write then Read: ");
|
||||
for (uint8_t i=0; i<2; i++) {
|
||||
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
Serial.print("0x");
|
||||
Serial.print(buffer[i], HEX);
|
||||
Serial.print(", ");
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
||||
void loop() {}
|
||||
+17
-12
@@ -1,38 +1,43 @@
|
||||
#include <Adafruit_I2CDevice.h>
|
||||
#include <Adafruit_BusIO_Register.h>
|
||||
#include <Adafruit_I2CDevice.h>
|
||||
|
||||
#define I2C_ADDRESS 0x60
|
||||
Adafruit_I2CDevice i2c_dev = Adafruit_I2CDevice(I2C_ADDRESS);
|
||||
|
||||
|
||||
void setup() {
|
||||
while (!Serial) { delay(10); }
|
||||
while (!Serial) {
|
||||
delay(10);
|
||||
}
|
||||
Serial.begin(115200);
|
||||
Serial.println("I2C device register test");
|
||||
|
||||
if (!i2c_dev.begin()) {
|
||||
Serial.print("Did not find device at 0x");
|
||||
Serial.println(i2c_dev.address(), HEX);
|
||||
while (1);
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
Serial.print("Device found on address 0x");
|
||||
Serial.println(i2c_dev.address(), HEX);
|
||||
|
||||
Adafruit_BusIO_Register id_reg = Adafruit_BusIO_Register(&i2c_dev, 0x0C, 2, LSBFIRST);
|
||||
Adafruit_BusIO_Register id_reg =
|
||||
Adafruit_BusIO_Register(&i2c_dev, 0x0C, 2, LSBFIRST);
|
||||
uint16_t id;
|
||||
id_reg.read(&id);
|
||||
Serial.print("ID register = 0x"); Serial.println(id, HEX);
|
||||
Serial.print("ID register = 0x");
|
||||
Serial.println(id, HEX);
|
||||
|
||||
Adafruit_BusIO_Register thresh_reg = Adafruit_BusIO_Register(&i2c_dev, 0x01, 2, LSBFIRST);
|
||||
Adafruit_BusIO_Register thresh_reg =
|
||||
Adafruit_BusIO_Register(&i2c_dev, 0x01, 2, LSBFIRST);
|
||||
uint16_t thresh;
|
||||
thresh_reg.read(&thresh);
|
||||
Serial.print("Initial threshold register = 0x"); Serial.println(thresh, HEX);
|
||||
Serial.print("Initial threshold register = 0x");
|
||||
Serial.println(thresh, HEX);
|
||||
|
||||
thresh_reg.write(~thresh);
|
||||
|
||||
Serial.print("Post threshold register = 0x"); Serial.println(thresh_reg.read(), HEX);
|
||||
Serial.print("Post threshold register = 0x");
|
||||
Serial.println(thresh_reg.read(), HEX);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
||||
void loop() {}
|
||||
+9
-7
@@ -9,7 +9,9 @@ Adafruit_SPIDevice *spi_dev = NULL; // new Adafruit_SPIDevice(SPIDEVICE_CS);
|
||||
Adafruit_I2CDevice *i2c_dev = new Adafruit_I2CDevice(I2C_ADDRESS);
|
||||
|
||||
void setup() {
|
||||
while (!Serial) { delay(10); }
|
||||
while (!Serial) {
|
||||
delay(10);
|
||||
}
|
||||
Serial.begin(115200);
|
||||
Serial.println("I2C or SPI device register test");
|
||||
|
||||
@@ -27,12 +29,12 @@ void setup() {
|
||||
}
|
||||
}
|
||||
|
||||
Adafruit_BusIO_Register id_reg = Adafruit_BusIO_Register(i2c_dev, spi_dev, ADDRBIT8_HIGH_TOREAD, 0x0F);
|
||||
uint8_t id=0;
|
||||
Adafruit_BusIO_Register id_reg =
|
||||
Adafruit_BusIO_Register(i2c_dev, spi_dev, ADDRBIT8_HIGH_TOREAD, 0x0F);
|
||||
uint8_t id = 0;
|
||||
id_reg.read(&id);
|
||||
Serial.print("ID register = 0x"); Serial.println(id, HEX);
|
||||
Serial.print("ID register = 0x");
|
||||
Serial.println(id, HEX);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
||||
void loop() {}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <Adafruit_SPIDevice.h>
|
||||
|
||||
#define SPIDEVICE_CS 10
|
||||
Adafruit_SPIDevice spi_dev =
|
||||
Adafruit_SPIDevice(SPIDEVICE_CS, 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1);
|
||||
// Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 13, 12, 11,
|
||||
// 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1);
|
||||
|
||||
void setup() {
|
||||
while (!Serial) {
|
||||
delay(10);
|
||||
}
|
||||
Serial.begin(115200);
|
||||
Serial.println("SPI device mode test");
|
||||
|
||||
if (!spi_dev.begin()) {
|
||||
Serial.println("Could not initialize SPI device");
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("\n\nTransfer test");
|
||||
for (uint16_t x = 0; x <= 0xFF; x++) {
|
||||
uint8_t i = x;
|
||||
Serial.print("0x");
|
||||
Serial.print(i, HEX);
|
||||
spi_dev.read(&i, 1, i);
|
||||
Serial.print("/");
|
||||
Serial.print(i, HEX);
|
||||
Serial.print(", ");
|
||||
delay(25);
|
||||
}
|
||||
}
|
||||
+15
-11
@@ -3,15 +3,17 @@
|
||||
#define SPIDEVICE_CS 10
|
||||
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS);
|
||||
|
||||
|
||||
void setup() {
|
||||
while (!Serial) { delay(10); }
|
||||
while (!Serial) {
|
||||
delay(10);
|
||||
}
|
||||
Serial.begin(115200);
|
||||
Serial.println("SPI device read and write test");
|
||||
|
||||
if (!spi_dev.begin()) {
|
||||
Serial.println("Could not initialize SPI device");
|
||||
while (1);
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
uint8_t buffer[32];
|
||||
@@ -19,21 +21,23 @@ void setup() {
|
||||
// Try to read 32 bytes
|
||||
spi_dev.read(buffer, 32);
|
||||
Serial.print("Read: ");
|
||||
for (uint8_t i=0; i<32; i++) {
|
||||
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
Serial.print("0x");
|
||||
Serial.print(buffer[i], HEX);
|
||||
Serial.print(", ");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// read a register by writing first, then reading
|
||||
buffer[0] = 0x8F; // we'll reuse the same buffer
|
||||
buffer[0] = 0x8F; // we'll reuse the same buffer
|
||||
spi_dev.write_then_read(buffer, 1, buffer, 2, false);
|
||||
Serial.print("Write then Read: ");
|
||||
for (uint8_t i=0; i<2; i++) {
|
||||
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
Serial.print("0x");
|
||||
Serial.print(buffer[i], HEX);
|
||||
Serial.print(", ");
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
||||
void loop() {}
|
||||
@@ -0,0 +1,268 @@
|
||||
/***************************************************
|
||||
|
||||
This is an example for how to use Adafruit_BusIO_RegisterBits from
|
||||
Adafruit_BusIO library.
|
||||
|
||||
Designed specifically to work with the Adafruit RTD Sensor
|
||||
----> https://www.adafruit.com/products/3328
|
||||
uisng a MAX31865 RTD-to-Digital Converter
|
||||
----> https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf
|
||||
|
||||
This sensor uses SPI to communicate, 4 pins are required to
|
||||
interface.
|
||||
A fifth pin helps to detect when a new conversion is ready.
|
||||
|
||||
Adafruit invests time and resources providing this open source code,
|
||||
please support Adafruit and open-source hardware by purchasing
|
||||
products from Adafruit!
|
||||
|
||||
Example written (2020/3) by Andreas Hardtung/AnHard.
|
||||
BSD license, all text above must be included in any redistribution
|
||||
****************************************************/
|
||||
|
||||
#include <Adafruit_BusIO_Register.h>
|
||||
#include <Adafruit_SPIDevice.h>
|
||||
|
||||
#define MAX31865_SPI_SPEED (5000000)
|
||||
#define MAX31865_SPI_BITORDER (SPI_BITORDER_MSBFIRST)
|
||||
#define MAX31865_SPI_MODE (SPI_MODE1)
|
||||
|
||||
#define MAX31865_SPI_CS (10)
|
||||
#define MAX31865_READY_PIN (2)
|
||||
|
||||
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(
|
||||
MAX31865_SPI_CS, MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER,
|
||||
MAX31865_SPI_MODE, &SPI); // Hardware SPI
|
||||
// Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice( MAX31865_SPI_CS, 13, 12, 11,
|
||||
// MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER, MAX31865_SPI_MODE); // Software
|
||||
// SPI
|
||||
|
||||
// MAX31865 chip related
|
||||
// *********************************************************************************************
|
||||
Adafruit_BusIO_Register config_reg =
|
||||
Adafruit_BusIO_Register(&spi_dev, 0x00, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits bias_bit =
|
||||
Adafruit_BusIO_RegisterBits(&config_reg, 1, 7);
|
||||
Adafruit_BusIO_RegisterBits auto_bit =
|
||||
Adafruit_BusIO_RegisterBits(&config_reg, 1, 6);
|
||||
Adafruit_BusIO_RegisterBits oneS_bit =
|
||||
Adafruit_BusIO_RegisterBits(&config_reg, 1, 5);
|
||||
Adafruit_BusIO_RegisterBits wire_bit =
|
||||
Adafruit_BusIO_RegisterBits(&config_reg, 1, 4);
|
||||
Adafruit_BusIO_RegisterBits faultT_bits =
|
||||
Adafruit_BusIO_RegisterBits(&config_reg, 2, 2);
|
||||
Adafruit_BusIO_RegisterBits faultR_bit =
|
||||
Adafruit_BusIO_RegisterBits(&config_reg, 1, 1);
|
||||
Adafruit_BusIO_RegisterBits fi50hz_bit =
|
||||
Adafruit_BusIO_RegisterBits(&config_reg, 1, 0);
|
||||
|
||||
Adafruit_BusIO_Register rRatio_reg =
|
||||
Adafruit_BusIO_Register(&spi_dev, 0x01, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits rRatio_bits =
|
||||
Adafruit_BusIO_RegisterBits(&rRatio_reg, 15, 1);
|
||||
Adafruit_BusIO_RegisterBits fault_bit =
|
||||
Adafruit_BusIO_RegisterBits(&rRatio_reg, 1, 0);
|
||||
|
||||
Adafruit_BusIO_Register maxRratio_reg =
|
||||
Adafruit_BusIO_Register(&spi_dev, 0x03, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits maxRratio_bits =
|
||||
Adafruit_BusIO_RegisterBits(&maxRratio_reg, 15, 1);
|
||||
|
||||
Adafruit_BusIO_Register minRratio_reg =
|
||||
Adafruit_BusIO_Register(&spi_dev, 0x05, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits minRratio_bits =
|
||||
Adafruit_BusIO_RegisterBits(&minRratio_reg, 15, 1);
|
||||
|
||||
Adafruit_BusIO_Register fault_reg =
|
||||
Adafruit_BusIO_Register(&spi_dev, 0x07, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits range_high_fault_bit =
|
||||
Adafruit_BusIO_RegisterBits(&fault_reg, 1, 7);
|
||||
Adafruit_BusIO_RegisterBits range_low_fault_bit =
|
||||
Adafruit_BusIO_RegisterBits(&fault_reg, 1, 6);
|
||||
Adafruit_BusIO_RegisterBits refin_high_fault_bit =
|
||||
Adafruit_BusIO_RegisterBits(&fault_reg, 1, 5);
|
||||
Adafruit_BusIO_RegisterBits refin_low_fault_bit =
|
||||
Adafruit_BusIO_RegisterBits(&fault_reg, 1, 4);
|
||||
Adafruit_BusIO_RegisterBits rtdin_low_fault_bit =
|
||||
Adafruit_BusIO_RegisterBits(&fault_reg, 1, 3);
|
||||
Adafruit_BusIO_RegisterBits voltage_fault_bit =
|
||||
Adafruit_BusIO_RegisterBits(&fault_reg, 1, 2);
|
||||
|
||||
// Print the details of the configuration register.
|
||||
void printConfig(void) {
|
||||
Serial.print("BIAS: ");
|
||||
if (bias_bit.read())
|
||||
Serial.print("ON");
|
||||
else
|
||||
Serial.print("OFF");
|
||||
Serial.print(", AUTO: ");
|
||||
if (auto_bit.read())
|
||||
Serial.print("ON");
|
||||
else
|
||||
Serial.print("OFF");
|
||||
Serial.print(", ONES: ");
|
||||
if (oneS_bit.read())
|
||||
Serial.print("ON");
|
||||
else
|
||||
Serial.print("OFF");
|
||||
Serial.print(", WIRE: ");
|
||||
if (wire_bit.read())
|
||||
Serial.print("3");
|
||||
else
|
||||
Serial.print("2/4");
|
||||
Serial.print(", FAULTCLEAR: ");
|
||||
if (faultR_bit.read())
|
||||
Serial.print("ON");
|
||||
else
|
||||
Serial.print("OFF");
|
||||
Serial.print(", ");
|
||||
if (fi50hz_bit.read())
|
||||
Serial.print("50HZ");
|
||||
else
|
||||
Serial.print("60HZ");
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
// Check and print faults. Then clear them.
|
||||
void checkFaults(void) {
|
||||
if (fault_bit.read()) {
|
||||
Serial.print("MAX: ");
|
||||
Serial.println(maxRratio_bits.read());
|
||||
Serial.print("VAL: ");
|
||||
Serial.println(rRatio_bits.read());
|
||||
Serial.print("MIN: ");
|
||||
Serial.println(minRratio_bits.read());
|
||||
|
||||
if (range_high_fault_bit.read())
|
||||
Serial.println("Range high fault");
|
||||
if (range_low_fault_bit.read())
|
||||
Serial.println("Range low fault");
|
||||
if (refin_high_fault_bit.read())
|
||||
Serial.println("REFIN high fault");
|
||||
if (refin_low_fault_bit.read())
|
||||
Serial.println("REFIN low fault");
|
||||
if (rtdin_low_fault_bit.read())
|
||||
Serial.println("RTDIN low fault");
|
||||
if (voltage_fault_bit.read())
|
||||
Serial.println("Voltage fault");
|
||||
|
||||
faultR_bit.write(1); // clear fault
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
#if (MAX31865_1_READY_PIN != -1)
|
||||
pinMode(MAX31865_READY_PIN, INPUT_PULLUP);
|
||||
#endif
|
||||
|
||||
while (!Serial) {
|
||||
delay(10);
|
||||
}
|
||||
Serial.begin(115200);
|
||||
Serial.println("SPI Adafruit_BusIO_RegisterBits test on MAX31865");
|
||||
|
||||
if (!spi_dev.begin()) {
|
||||
Serial.println("Could not initialize SPI device");
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
// Set up for automode 50Hz. We don't care about selfheating. We want the
|
||||
// highest possible sampling rate.
|
||||
auto_bit.write(0); // Don't switch filtermode while auto_mode is on.
|
||||
fi50hz_bit.write(1); // Set filter to 50Hz mode.
|
||||
faultR_bit.write(1); // Clear faults.
|
||||
bias_bit.write(1); // In automode we want to have the bias current always on.
|
||||
delay(5); // Wait until bias current settles down.
|
||||
// 10.5 time constants of the input RC network is required.
|
||||
// 10ms worst case for 10kω reference resistor and a 0.1µF capacitor
|
||||
// across the RTD inputs. Adafruit Module has 0.1µF and only
|
||||
// 430/4300ω So here 0.43/4.3ms
|
||||
auto_bit.write(
|
||||
1); // Now we can set automode. Automatically starting first conversion.
|
||||
|
||||
// Test the READY_PIN
|
||||
#if (defined(MAX31865_READY_PIN) && (MAX31865_READY_PIN != -1))
|
||||
int i = 0;
|
||||
while (digitalRead(MAX31865_READY_PIN) && i++ <= 100) {
|
||||
delay(1);
|
||||
}
|
||||
if (i >= 100) {
|
||||
Serial.print("ERROR: Max31865 Pin detection does not work. PIN:");
|
||||
Serial.println(MAX31865_READY_PIN);
|
||||
}
|
||||
#else
|
||||
delay(100);
|
||||
#endif
|
||||
|
||||
// Set ratio range.
|
||||
// Setting the temperatures would need some more calculation - not related to
|
||||
// Adafruit_BusIO_RegisterBits.
|
||||
uint16_t ratio = rRatio_bits.read();
|
||||
maxRratio_bits.write((ratio < 0x8fffu - 1000u) ? ratio + 1000u : 0x8fffu);
|
||||
minRratio_bits.write((ratio > 1000u) ? ratio - 1000u : 0u);
|
||||
|
||||
printConfig();
|
||||
checkFaults();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
#if (defined(MAX31865_READY_PIN) && (MAX31865_1_READY_PIN != -1))
|
||||
// Is conversion ready?
|
||||
if (!digitalRead(MAX31865_READY_PIN))
|
||||
#else
|
||||
// Warant conversion is ready.
|
||||
delay(21); // 21ms for 50Hz-mode. 19ms in 60Hz-mode.
|
||||
#endif
|
||||
{
|
||||
// Read ratio, calculate temperature, scale, filter and print.
|
||||
Serial.println(rRatio2C(rRatio_bits.read()) * 100.0f,
|
||||
0); // Temperature scaled by 100
|
||||
// Check, print, clear faults.
|
||||
checkFaults();
|
||||
}
|
||||
|
||||
// Do something else.
|
||||
// delay(15000);
|
||||
}
|
||||
|
||||
// Module/Sensor related. Here Adafruit PT100 module with a 2_Wire PT100 Class C
|
||||
// *****************************
|
||||
float rRatio2C(uint16_t ratio) {
|
||||
// A simple linear conversion.
|
||||
const float R0 = 100.0f;
|
||||
const float Rref = 430.0f;
|
||||
const float alphaPT = 0.003850f;
|
||||
const float ADCmax = (1u << 15) - 1.0f;
|
||||
const float rscale = Rref / ADCmax;
|
||||
// Measured temperature in boiling water 101.08°C with factor a = 1 and b = 0.
|
||||
// Rref and MAX at about 22±2°C. Measured temperature in ice/water bath 0.76°C
|
||||
// with factor a = 1 and b = 0. Rref and MAX at about 22±2°C.
|
||||
// const float a = 1.0f / (alphaPT * R0);
|
||||
const float a = (100.0f / 101.08f) / (alphaPT * R0);
|
||||
// const float b = 0.0f; // 101.08
|
||||
const float b = -0.76f; // 100.32 > 101.08
|
||||
|
||||
return filterRing(((ratio * rscale) - R0) * a + b);
|
||||
}
|
||||
|
||||
// General purpose
|
||||
// *********************************************************************************************
|
||||
#define RINGLENGTH 250
|
||||
float filterRing(float newVal) {
|
||||
static float ring[RINGLENGTH] = {0.0};
|
||||
static uint8_t ringIndex = 0;
|
||||
static bool ringFull = false;
|
||||
|
||||
if (ringIndex == RINGLENGTH) {
|
||||
ringFull = true;
|
||||
ringIndex = 0;
|
||||
}
|
||||
ring[ringIndex] = newVal;
|
||||
uint8_t loopEnd = (ringFull) ? RINGLENGTH : ringIndex + 1;
|
||||
float ringSum = 0.0f;
|
||||
for (uint8_t i = 0; i < loopEnd; i++)
|
||||
ringSum += ring[i];
|
||||
ringIndex++;
|
||||
return ringSum / loopEnd;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <Adafruit_BusIO_Register.h>
|
||||
#include <Adafruit_SPIDevice.h>
|
||||
|
||||
#define SPIDEVICE_CS 10
|
||||
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS);
|
||||
|
||||
void setup() {
|
||||
while (!Serial) {
|
||||
delay(10);
|
||||
}
|
||||
Serial.begin(115200);
|
||||
Serial.println("SPI device register test");
|
||||
|
||||
if (!spi_dev.begin()) {
|
||||
Serial.println("Could not initialize SPI device");
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
Adafruit_BusIO_Register id_reg =
|
||||
Adafruit_BusIO_Register(&spi_dev, 0x0F, ADDRBIT8_HIGH_TOREAD);
|
||||
uint8_t id = 0;
|
||||
id_reg.read(&id);
|
||||
Serial.print("ID register = 0x");
|
||||
Serial.println(id, HEX);
|
||||
|
||||
Adafruit_BusIO_Register thresh_reg = Adafruit_BusIO_Register(
|
||||
&spi_dev, 0x0C, ADDRBIT8_HIGH_TOREAD, 2, LSBFIRST);
|
||||
uint16_t thresh = 0;
|
||||
thresh_reg.read(&thresh);
|
||||
Serial.print("Initial threshold register = 0x");
|
||||
Serial.println(thresh, HEX);
|
||||
|
||||
thresh_reg.write(~thresh);
|
||||
|
||||
Serial.print("Post threshold register = 0x");
|
||||
Serial.println(thresh_reg.read(), HEX);
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name=Adafruit BusIO
|
||||
version=1.11.0
|
||||
version=1.17.4
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=This is a library for abstracting away UART, I2C and SPI interfacing
|
||||
@@ -1,29 +0,0 @@
|
||||
#include <Adafruit_SPIDevice.h>
|
||||
|
||||
#define SPIDEVICE_CS 10
|
||||
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1);
|
||||
//Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 13, 12, 11, 100000, SPI_BITORDER_MSBFIRST, SPI_MODE1);
|
||||
|
||||
|
||||
void setup() {
|
||||
while (!Serial) { delay(10); }
|
||||
Serial.begin(115200);
|
||||
Serial.println("SPI device mode test");
|
||||
|
||||
if (!spi_dev.begin()) {
|
||||
Serial.println("Could not initialize SPI device");
|
||||
while (1);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("\n\nTransfer test");
|
||||
for (uint16_t x=0; x<=0xFF; x++) {
|
||||
uint8_t i = x;
|
||||
Serial.print("0x"); Serial.print(i, HEX);
|
||||
spi_dev.read(&i, 1, i);
|
||||
Serial.print("/"); Serial.print(i, HEX);
|
||||
Serial.print(", ");
|
||||
delay(25);
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/***************************************************
|
||||
|
||||
This is an example for how to use Adafruit_BusIO_RegisterBits from Adafruit_BusIO library.
|
||||
|
||||
Designed specifically to work with the Adafruit RTD Sensor
|
||||
----> https://www.adafruit.com/products/3328
|
||||
uisng a MAX31865 RTD-to-Digital Converter
|
||||
----> https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf
|
||||
|
||||
This sensor uses SPI to communicate, 4 pins are required to
|
||||
interface.
|
||||
A fifth pin helps to detect when a new conversion is ready.
|
||||
|
||||
Adafruit invests time and resources providing this open source code,
|
||||
please support Adafruit and open-source hardware by purchasing
|
||||
products from Adafruit!
|
||||
|
||||
Example written (2020/3) by Andreas Hardtung/AnHard.
|
||||
BSD license, all text above must be included in any redistribution
|
||||
****************************************************/
|
||||
|
||||
#include <Adafruit_BusIO_Register.h>
|
||||
#include <Adafruit_SPIDevice.h>
|
||||
|
||||
#define MAX31865_SPI_SPEED (5000000)
|
||||
#define MAX31865_SPI_BITORDER (SPI_BITORDER_MSBFIRST)
|
||||
#define MAX31865_SPI_MODE (SPI_MODE1)
|
||||
|
||||
#define MAX31865_SPI_CS (10)
|
||||
#define MAX31865_READY_PIN (2)
|
||||
|
||||
|
||||
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice( MAX31865_SPI_CS, MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER, MAX31865_SPI_MODE, &SPI); // Hardware SPI
|
||||
// Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice( MAX31865_SPI_CS, 13, 12, 11, MAX31865_SPI_SPEED, MAX31865_SPI_BITORDER, MAX31865_SPI_MODE); // Software SPI
|
||||
|
||||
// MAX31865 chip related *********************************************************************************************
|
||||
Adafruit_BusIO_Register config_reg = Adafruit_BusIO_Register(&spi_dev, 0x00, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits bias_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 7);
|
||||
Adafruit_BusIO_RegisterBits auto_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 6);
|
||||
Adafruit_BusIO_RegisterBits oneS_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 5);
|
||||
Adafruit_BusIO_RegisterBits wire_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 4);
|
||||
Adafruit_BusIO_RegisterBits faultT_bits = Adafruit_BusIO_RegisterBits(&config_reg, 2, 2);
|
||||
Adafruit_BusIO_RegisterBits faultR_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 1);
|
||||
Adafruit_BusIO_RegisterBits fi50hz_bit = Adafruit_BusIO_RegisterBits(&config_reg, 1, 0);
|
||||
|
||||
Adafruit_BusIO_Register rRatio_reg = Adafruit_BusIO_Register(&spi_dev, 0x01, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits rRatio_bits = Adafruit_BusIO_RegisterBits(&rRatio_reg, 15, 1);
|
||||
Adafruit_BusIO_RegisterBits fault_bit = Adafruit_BusIO_RegisterBits(&rRatio_reg, 1, 0);
|
||||
|
||||
Adafruit_BusIO_Register maxRratio_reg = Adafruit_BusIO_Register(&spi_dev, 0x03, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits maxRratio_bits = Adafruit_BusIO_RegisterBits(&maxRratio_reg, 15, 1);
|
||||
|
||||
Adafruit_BusIO_Register minRratio_reg = Adafruit_BusIO_Register(&spi_dev, 0x05, ADDRBIT8_HIGH_TOWRITE, 2, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits minRratio_bits = Adafruit_BusIO_RegisterBits(&minRratio_reg, 15, 1);
|
||||
|
||||
Adafruit_BusIO_Register fault_reg = Adafruit_BusIO_Register(&spi_dev, 0x07, ADDRBIT8_HIGH_TOWRITE, 1, MSBFIRST);
|
||||
Adafruit_BusIO_RegisterBits range_high_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 7);
|
||||
Adafruit_BusIO_RegisterBits range_low_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 6);
|
||||
Adafruit_BusIO_RegisterBits refin_high_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 5);
|
||||
Adafruit_BusIO_RegisterBits refin_low_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 4);
|
||||
Adafruit_BusIO_RegisterBits rtdin_low_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 3);
|
||||
Adafruit_BusIO_RegisterBits voltage_fault_bit = Adafruit_BusIO_RegisterBits(&fault_reg, 1, 2);
|
||||
|
||||
// Print the details of the configuration register.
|
||||
void printConfig( void ) {
|
||||
Serial.print("BIAS: "); if (bias_bit.read() ) Serial.print("ON"); else Serial.print("OFF");
|
||||
Serial.print(", AUTO: "); if (auto_bit.read() ) Serial.print("ON"); else Serial.print("OFF");
|
||||
Serial.print(", ONES: "); if (oneS_bit.read() ) Serial.print("ON"); else Serial.print("OFF");
|
||||
Serial.print(", WIRE: "); if (wire_bit.read() ) Serial.print("3"); else Serial.print("2/4");
|
||||
Serial.print(", FAULTCLEAR: "); if (faultR_bit.read() ) Serial.print("ON"); else Serial.print("OFF");
|
||||
Serial.print(", "); if (fi50hz_bit.read() ) Serial.print("50HZ"); else Serial.print("60HZ");
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
// Check and print faults. Then clear them.
|
||||
void checkFaults( void ) {
|
||||
if (fault_bit.read()) {
|
||||
Serial.print("MAX: "); Serial.println(maxRratio_bits.read());
|
||||
Serial.print("VAL: "); Serial.println( rRatio_bits.read());
|
||||
Serial.print("MIN: "); Serial.println(minRratio_bits.read());
|
||||
|
||||
if (range_high_fault_bit.read() ) Serial.println("Range high fault");
|
||||
if ( range_low_fault_bit.read() ) Serial.println("Range low fault");
|
||||
if (refin_high_fault_bit.read() ) Serial.println("REFIN high fault");
|
||||
if ( refin_low_fault_bit.read() ) Serial.println("REFIN low fault");
|
||||
if ( rtdin_low_fault_bit.read() ) Serial.println("RTDIN low fault");
|
||||
if ( voltage_fault_bit.read() ) Serial.println("Voltage fault");
|
||||
|
||||
faultR_bit.write(1); // clear fault
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
#if (MAX31865_1_READY_PIN != -1)
|
||||
pinMode(MAX31865_READY_PIN ,INPUT_PULLUP);
|
||||
#endif
|
||||
|
||||
while (!Serial) { delay(10); }
|
||||
Serial.begin(115200);
|
||||
Serial.println("SPI Adafruit_BusIO_RegisterBits test on MAX31865");
|
||||
|
||||
if (!spi_dev.begin()) {
|
||||
Serial.println("Could not initialize SPI device");
|
||||
while (1);
|
||||
}
|
||||
|
||||
// Set up for automode 50Hz. We don't care about selfheating. We want the highest possible sampling rate.
|
||||
auto_bit.write(0); // Don't switch filtermode while auto_mode is on.
|
||||
fi50hz_bit.write(1); // Set filter to 50Hz mode.
|
||||
faultR_bit.write(1); // Clear faults.
|
||||
bias_bit.write(1); // In automode we want to have the bias current always on.
|
||||
delay(5); // Wait until bias current settles down.
|
||||
// 10.5 time constants of the input RC network is required.
|
||||
// 10ms worst case for 10kω reference resistor and a 0.1µF capacitor across the RTD inputs.
|
||||
// Adafruit Module has 0.1µF and only 430/4300ω So here 0.43/4.3ms
|
||||
auto_bit.write(1); // Now we can set automode. Automatically starting first conversion.
|
||||
|
||||
// Test the READY_PIN
|
||||
#if (defined( MAX31865_READY_PIN ) && (MAX31865_READY_PIN != -1))
|
||||
int i = 0;
|
||||
while (digitalRead(MAX31865_READY_PIN) && i++ <= 100) { delay(1); }
|
||||
if (i >= 100) {
|
||||
Serial.print("ERROR: Max31865 Pin detection does not work. PIN:");
|
||||
Serial.println(MAX31865_READY_PIN);
|
||||
}
|
||||
#else
|
||||
delay(100);
|
||||
#endif
|
||||
|
||||
// Set ratio range.
|
||||
// Setting the temperatures would need some more calculation - not related to Adafruit_BusIO_RegisterBits.
|
||||
uint16_t ratio = rRatio_bits.read();
|
||||
maxRratio_bits.write( (ratio < 0x8fffu-1000u) ? ratio + 1000u : 0x8fffu );
|
||||
minRratio_bits.write( (ratio > 1000u) ? ratio - 1000u : 0u );
|
||||
|
||||
printConfig();
|
||||
checkFaults();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
#if (defined( MAX31865_READY_PIN ) && (MAX31865_1_READY_PIN != -1))
|
||||
// Is conversion ready?
|
||||
if (!digitalRead(MAX31865_READY_PIN))
|
||||
#else
|
||||
// Warant conversion is ready.
|
||||
delay(21); // 21ms for 50Hz-mode. 19ms in 60Hz-mode.
|
||||
#endif
|
||||
{
|
||||
// Read ratio, calculate temperature, scale, filter and print.
|
||||
Serial.println( rRatio2C( rRatio_bits.read() ) * 100.0f, 0); // Temperature scaled by 100
|
||||
// Check, print, clear faults.
|
||||
checkFaults();
|
||||
}
|
||||
|
||||
// Do something else.
|
||||
//delay(15000);
|
||||
}
|
||||
|
||||
|
||||
// Module/Sensor related. Here Adafruit PT100 module with a 2_Wire PT100 Class C *****************************
|
||||
float rRatio2C(uint16_t ratio) {
|
||||
// A simple linear conversion.
|
||||
const float R0 = 100.0f;
|
||||
const float Rref = 430.0f;
|
||||
const float alphaPT = 0.003850f;
|
||||
const float ADCmax = (1u << 15) - 1.0f;
|
||||
const float rscale = Rref / ADCmax;
|
||||
// Measured temperature in boiling water 101.08°C with factor a = 1 and b = 0. Rref and MAX at about 22±2°C.
|
||||
// Measured temperature in ice/water bath 0.76°C with factor a = 1 and b = 0. Rref and MAX at about 22±2°C.
|
||||
//const float a = 1.0f / (alphaPT * R0);
|
||||
const float a = (100.0f/101.08f) / (alphaPT * R0);
|
||||
//const float b = 0.0f; // 101.08
|
||||
const float b = -0.76f; // 100.32 > 101.08
|
||||
|
||||
return filterRing( ((ratio * rscale) - R0) * a + b );
|
||||
}
|
||||
|
||||
// General purpose *********************************************************************************************
|
||||
#define RINGLENGTH 250
|
||||
float filterRing( float newVal ) {
|
||||
static float ring[RINGLENGTH] = { 0.0 };
|
||||
static uint8_t ringIndex = 0;
|
||||
static bool ringFull = false;
|
||||
|
||||
if ( ringIndex == RINGLENGTH ) { ringFull = true; ringIndex = 0; }
|
||||
ring[ringIndex] = newVal;
|
||||
uint8_t loopEnd = (ringFull) ? RINGLENGTH : ringIndex + 1;
|
||||
float ringSum = 0.0f;
|
||||
for (uint8_t i = 0; i < loopEnd; i++) ringSum += ring[i];
|
||||
ringIndex++;
|
||||
return ringSum / loopEnd;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#include <Adafruit_BusIO_Register.h>
|
||||
#include <Adafruit_SPIDevice.h>
|
||||
|
||||
#define SPIDEVICE_CS 10
|
||||
Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS);
|
||||
|
||||
void setup() {
|
||||
while (!Serial) { delay(10); }
|
||||
Serial.begin(115200);
|
||||
Serial.println("SPI device register test");
|
||||
|
||||
if (!spi_dev.begin()) {
|
||||
Serial.println("Could not initialize SPI device");
|
||||
while (1);
|
||||
}
|
||||
|
||||
Adafruit_BusIO_Register id_reg = Adafruit_BusIO_Register(&spi_dev, 0x0F, ADDRBIT8_HIGH_TOREAD);
|
||||
uint8_t id = 0;
|
||||
id_reg.read(&id);
|
||||
Serial.print("ID register = 0x"); Serial.println(id, HEX);
|
||||
|
||||
Adafruit_BusIO_Register thresh_reg = Adafruit_BusIO_Register(&spi_dev, 0x0C, ADDRBIT8_HIGH_TOREAD, 2, LSBFIRST);
|
||||
uint16_t thresh = 0;
|
||||
thresh_reg.read(&thresh);
|
||||
Serial.print("Initial threshold register = 0x"); Serial.println(thresh, HEX);
|
||||
|
||||
thresh_reg.write(~thresh);
|
||||
|
||||
Serial.print("Post threshold register = 0x"); Serial.println(thresh_reg.read(), HEX);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
language: c
|
||||
sudo: false
|
||||
|
||||
# Blacklist
|
||||
branches:
|
||||
except:
|
||||
- gh-pages
|
||||
|
||||
env:
|
||||
global:
|
||||
- PRETTYNAME="Adafruit CCS811 Arduino Library"
|
||||
# Optional, will default to "$TRAVIS_BUILD_DIR/Doxyfile"
|
||||
# - DOXYFILE: $TRAVIS_BUILD_DIR/Doxyfile
|
||||
|
||||
before_install:
|
||||
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh)
|
||||
|
||||
install:
|
||||
- arduino --install-library "Adafruit SSD1306","Adafruit GFX Library"
|
||||
|
||||
script:
|
||||
- build_main_platforms
|
||||
|
||||
# Generate and deploy documentation
|
||||
after_success:
|
||||
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/library_check.sh)
|
||||
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/doxy_gen_and_deploy.sh)
|
||||
@@ -1,280 +0,0 @@
|
||||
#include "Adafruit_CCS811.h"
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Setups the I2C interface and hardware and checks for communication.
|
||||
@param addr Optional I2C address the sensor can be found on. Default is 0x5A
|
||||
@returns True if device is set up, false on any failure
|
||||
*/
|
||||
/**************************************************************************/
|
||||
sint8_t Adafruit_CCS811::begin(uint8_t addr)
|
||||
{
|
||||
_i2caddr = addr;
|
||||
|
||||
_i2c_init();
|
||||
|
||||
SWReset();
|
||||
delay(100);
|
||||
|
||||
//check that the HW id is correct
|
||||
if(this->read8(CCS811_HW_ID) != CCS811_HW_ID_CODE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//try to start the app
|
||||
this->write(CCS811_BOOTLOADER_APP_START, NULL, 0);
|
||||
delay(100);
|
||||
|
||||
//make sure there are no errors and we have entered application mode
|
||||
if(checkError()) {
|
||||
return -2;
|
||||
}
|
||||
if(!_status.FW_MODE) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
disableInterrupt();
|
||||
|
||||
//default to read every second
|
||||
setDriveMode(CCS811_DRIVE_MODE_1SEC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief sample rate of the sensor.
|
||||
@param mode one of CCS811_DRIVE_MODE_IDLE, CCS811_DRIVE_MODE_1SEC, CCS811_DRIVE_MODE_10SEC, CCS811_DRIVE_MODE_60SEC, CCS811_DRIVE_MODE_250MS.
|
||||
*/
|
||||
void Adafruit_CCS811::setDriveMode(uint8_t mode)
|
||||
{
|
||||
_meas_mode.DRIVE_MODE = mode;
|
||||
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief enable the data ready interrupt pin on the device.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::enableInterrupt()
|
||||
{
|
||||
_meas_mode.INT_DATARDY = 1;
|
||||
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief disable the data ready interrupt pin on the device
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::disableInterrupt()
|
||||
{
|
||||
_meas_mode.INT_DATARDY = 0;
|
||||
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief checks if data is available to be read.
|
||||
@returns True if data is ready, false otherwise.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
bool Adafruit_CCS811::available()
|
||||
{
|
||||
_status.set(read8(CCS811_STATUS));
|
||||
if(!_status.DATA_READY)
|
||||
return false;
|
||||
else return true;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief read and store the sensor data. This data can be accessed with getTVOC() and geteCO2()
|
||||
@returns 0 if no error, error code otherwise.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint8_t Adafruit_CCS811::readData()
|
||||
{
|
||||
if(!available())
|
||||
return false;
|
||||
else{
|
||||
uint8_t buf[8];
|
||||
this->read(CCS811_ALG_RESULT_DATA, buf, 8);
|
||||
|
||||
_eCO2 = ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
|
||||
_TVOC = ((uint16_t)buf[2] << 8) | ((uint16_t)buf[3]);
|
||||
|
||||
if(_status.ERROR)
|
||||
return buf[5];
|
||||
|
||||
else return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief set the humidity and temperature compensation for the sensor.
|
||||
@param humidity the humidity data as a percentage. For 55% humidity, pass in integer 55.
|
||||
@param temperature the temperature in degrees C as a decimal number. For 25.5 degrees C, pass in 25.5
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::setEnvironmentalData(uint8_t humidity, double temperature)
|
||||
{
|
||||
/* Humidity is stored as an unsigned 16 bits in 1/512%RH. The
|
||||
default value is 50% = 0x64, 0x00. As an example 48.5%
|
||||
humidity would be 0x61, 0x00.*/
|
||||
|
||||
/* Temperature is stored as an unsigned 16 bits integer in 1/512
|
||||
degrees; there is an offset: 0 maps to -25°C. The default value is
|
||||
25°C = 0x64, 0x00. As an example 23.5% temperature would be
|
||||
0x61, 0x00.
|
||||
The internal algorithm uses these values (or default values if
|
||||
not set by the application) to compensate for changes in
|
||||
relative humidity and ambient temperature.*/
|
||||
|
||||
uint8_t hum_perc = humidity << 1;
|
||||
|
||||
float fractional = modf(temperature, &temperature);
|
||||
uint16_t temp_high = (((uint16_t)temperature + 25) << 9);
|
||||
uint16_t temp_low = ((uint16_t)(fractional / 0.001953125) & 0x1FF);
|
||||
|
||||
uint16_t temp_conv = (temp_high | temp_low);
|
||||
|
||||
uint8_t buf[] = {hum_perc, 0x00,
|
||||
(uint8_t)((temp_conv >> 8) & 0xFF), (uint8_t)(temp_conv & 0xFF)};
|
||||
|
||||
this->write(CCS811_ENV_DATA, buf, 4);
|
||||
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief calculate the temperature using the onboard NTC resistor.
|
||||
@returns temperature as a double.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
double Adafruit_CCS811::calculateTemperature()
|
||||
{
|
||||
uint8_t buf[4];
|
||||
this->read(CCS811_NTC, buf, 4);
|
||||
|
||||
uint32_t vref = ((uint32_t)buf[0] << 8) | buf[1];
|
||||
uint32_t vntc = ((uint32_t)buf[2] << 8) | buf[3];
|
||||
|
||||
//from ams ccs811 app note
|
||||
uint32_t rntc = vntc * CCS811_REF_RESISTOR / vref;
|
||||
|
||||
double ntc_temp;
|
||||
ntc_temp = log((double)rntc / CCS811_REF_RESISTOR); // 1
|
||||
ntc_temp /= 3380; // 2
|
||||
ntc_temp += 1.0 / (25 + 273.15); // 3
|
||||
ntc_temp = 1.0 / ntc_temp; // 4
|
||||
ntc_temp -= 273.15; // 5
|
||||
return ntc_temp - _tempOffset;
|
||||
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief set interrupt thresholds
|
||||
@param low_med the level below which an interrupt will be triggered.
|
||||
@param med_high the level above which the interrupt will ge triggered.
|
||||
@param hysteresis optional histeresis level. Defaults to 50
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::setThresholds(uint16_t low_med, uint16_t med_high, uint8_t hysteresis)
|
||||
{
|
||||
uint8_t buf[] = {(uint8_t)((low_med >> 8) & 0xF), (uint8_t)(low_med & 0xF),
|
||||
(uint8_t)((med_high >> 8) & 0xF), (uint8_t)(med_high & 0xF), hysteresis};
|
||||
|
||||
this->write(CCS811_THRESHOLDS, buf, 5);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief trigger a software reset of the device
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::SWReset()
|
||||
{
|
||||
//reset sequence from the datasheet
|
||||
uint8_t seq[] = {0x11, 0xE5, 0x72, 0x8A};
|
||||
this->write(CCS811_SW_RESET, seq, 4);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief read the status register and store any errors.
|
||||
@returns the error bits from the status register of the device.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
bool Adafruit_CCS811::checkError()
|
||||
{
|
||||
_status.set(read8(CCS811_STATUS));
|
||||
return _status.ERROR;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief write one byte of data to the specified register
|
||||
@param reg the register to write to
|
||||
@param value the value to write
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::write8(byte reg, byte value)
|
||||
{
|
||||
this->write(reg, &value, 1);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief read one byte of data from the specified register
|
||||
@param reg the register to read
|
||||
@returns one byte of register data
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint8_t Adafruit_CCS811::read8(byte reg)
|
||||
{
|
||||
uint8_t ret;
|
||||
this->read(reg, &ret, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Adafruit_CCS811::_i2c_init()
|
||||
{
|
||||
Wire.begin();
|
||||
#ifdef ESP8266
|
||||
Wire.setClockStretchLimit(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Adafruit_CCS811::read(uint8_t reg, uint8_t *buf, uint8_t num)
|
||||
{
|
||||
uint8_t value;
|
||||
uint8_t pos = 0;
|
||||
|
||||
//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)_i2caddr);
|
||||
Wire.write((uint8_t)reg + pos);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom((uint8_t)_i2caddr, read_now);
|
||||
|
||||
for(int i=0; i<read_now; i++){
|
||||
buf[pos] = Wire.read();
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Adafruit_CCS811::write(uint8_t reg, uint8_t *buf, uint8_t num)
|
||||
{
|
||||
Wire.beginTransmission((uint8_t)_i2caddr);
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.write((uint8_t *)buf, num);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
#ifndef LIB_ADAFRUIT_CCS811_H
|
||||
#define LIB_ADAFRUIT_CCS811_H
|
||||
|
||||
#if (ARDUINO >= 100)
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
/*=========================================================================
|
||||
I2C ADDRESS/BITS
|
||||
-----------------------------------------------------------------------*/
|
||||
#define CCS811_ADDRESS (0x5A)
|
||||
/*=========================================================================*/
|
||||
|
||||
/*=========================================================================
|
||||
REGISTERS
|
||||
-----------------------------------------------------------------------*/
|
||||
enum
|
||||
{
|
||||
CCS811_STATUS = 0x00,
|
||||
CCS811_MEAS_MODE = 0x01,
|
||||
CCS811_ALG_RESULT_DATA = 0x02,
|
||||
CCS811_RAW_DATA = 0x03,
|
||||
CCS811_ENV_DATA = 0x05,
|
||||
CCS811_NTC = 0x06,
|
||||
CCS811_THRESHOLDS = 0x10,
|
||||
CCS811_BASELINE = 0x11,
|
||||
CCS811_HW_ID = 0x20,
|
||||
CCS811_HW_VERSION = 0x21,
|
||||
CCS811_FW_BOOT_VERSION = 0x23,
|
||||
CCS811_FW_APP_VERSION = 0x24,
|
||||
CCS811_ERROR_ID = 0xE0,
|
||||
CCS811_SW_RESET = 0xFF,
|
||||
};
|
||||
|
||||
//bootloader registers
|
||||
enum
|
||||
{
|
||||
CCS811_BOOTLOADER_APP_ERASE = 0xF1,
|
||||
CCS811_BOOTLOADER_APP_DATA = 0xF2,
|
||||
CCS811_BOOTLOADER_APP_VERIFY = 0xF3,
|
||||
CCS811_BOOTLOADER_APP_START = 0xF4
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CCS811_DRIVE_MODE_IDLE = 0x00,
|
||||
CCS811_DRIVE_MODE_1SEC = 0x01,
|
||||
CCS811_DRIVE_MODE_10SEC = 0x02,
|
||||
CCS811_DRIVE_MODE_60SEC = 0x03,
|
||||
CCS811_DRIVE_MODE_250MS = 0x04,
|
||||
};
|
||||
|
||||
/*=========================================================================*/
|
||||
|
||||
#define CCS811_HW_ID_CODE 0x81
|
||||
|
||||
#define CCS811_REF_RESISTOR 100000
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Class that stores state and functions for interacting with CCS811 gas sensor chips
|
||||
*/
|
||||
/**************************************************************************/
|
||||
class Adafruit_CCS811 {
|
||||
public:
|
||||
//constructors
|
||||
Adafruit_CCS811(void) {};
|
||||
~Adafruit_CCS811(void) {};
|
||||
|
||||
sint8_t begin(uint8_t addr = CCS811_ADDRESS);
|
||||
|
||||
void setEnvironmentalData(uint8_t humidity, double temperature);
|
||||
|
||||
//calculate temperature based on the NTC register
|
||||
double calculateTemperature();
|
||||
|
||||
void setThresholds(uint16_t low_med, uint16_t med_high, uint8_t hysteresis = 50);
|
||||
|
||||
void SWReset();
|
||||
|
||||
void setDriveMode(uint8_t mode);
|
||||
void enableInterrupt();
|
||||
void disableInterrupt();
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief returns the stored total volatile organic compounds measurement. This does does not read the sensor. To do so, call readData()
|
||||
@returns TVOC measurement as 16 bit integer
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint16_t getTVOC() { return _TVOC; }
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief returns the stored estimated carbon dioxide measurement. This does does not read the sensor. To do so, call readData()
|
||||
@returns eCO2 measurement as 16 bit integer
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint16_t geteCO2() { return _eCO2; }
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief set the temperature compensation offset for the device. This is needed to offset errors in NTC measurements.
|
||||
@param offset the offset to be added to temperature measurements.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void setTempOffset(float offset) { _tempOffset = offset; }
|
||||
|
||||
//check if data is available to be read
|
||||
bool available();
|
||||
uint8_t readData();
|
||||
|
||||
bool checkError();
|
||||
|
||||
private:
|
||||
uint8_t _i2caddr;
|
||||
float _tempOffset;
|
||||
|
||||
uint16_t _TVOC;
|
||||
uint16_t _eCO2;
|
||||
|
||||
void write8(byte reg, byte value);
|
||||
void write16(byte reg, uint16_t value);
|
||||
uint8_t read8(byte reg);
|
||||
|
||||
void read(uint8_t reg, uint8_t *buf, uint8_t num);
|
||||
void write(uint8_t reg, uint8_t *buf, uint8_t num);
|
||||
void _i2c_init();
|
||||
|
||||
/*=========================================================================
|
||||
REGISTER BITFIELDS
|
||||
-----------------------------------------------------------------------*/
|
||||
// The status register
|
||||
struct status {
|
||||
|
||||
/* 0: no error
|
||||
* 1: error has occurred
|
||||
*/
|
||||
uint8_t ERROR: 1;
|
||||
|
||||
// reserved : 2
|
||||
|
||||
/* 0: no samples are ready
|
||||
* 1: samples are ready
|
||||
*/
|
||||
uint8_t DATA_READY: 1;
|
||||
uint8_t APP_VALID: 1;
|
||||
|
||||
// reserved : 2
|
||||
|
||||
/* 0: boot mode, new firmware can be loaded
|
||||
* 1: application mode, can take measurements
|
||||
*/
|
||||
uint8_t FW_MODE: 1;
|
||||
|
||||
void set(uint8_t data){
|
||||
ERROR = data & 0x01;
|
||||
DATA_READY = (data >> 3) & 0x01;
|
||||
APP_VALID = (data >> 4) & 0x01;
|
||||
FW_MODE = (data >> 7) & 0x01;
|
||||
}
|
||||
};
|
||||
status _status;
|
||||
|
||||
//measurement and conditions register
|
||||
struct meas_mode {
|
||||
// reserved : 2
|
||||
|
||||
/* 0: interrupt mode operates normally
|
||||
* 1: Interrupt mode (if enabled) only asserts the nINT signal (driven low) if the new
|
||||
ALG_RESULT_DATA crosses one of the thresholds set in the THRESHOLDS register
|
||||
by more than the hysteresis value (also in the THRESHOLDS register)
|
||||
*/
|
||||
uint8_t INT_THRESH: 1;
|
||||
|
||||
/* 0: int disabled
|
||||
* 1: The nINT signal is asserted (driven low) when a new sample is ready in
|
||||
ALG_RESULT_DATA. The nINT signal will stop being driven low when
|
||||
ALG_RESULT_DATA is read on the I²C interface.
|
||||
*/
|
||||
uint8_t INT_DATARDY: 1;
|
||||
|
||||
uint8_t DRIVE_MODE: 3;
|
||||
|
||||
uint8_t get(){
|
||||
return (INT_THRESH << 2) | (INT_DATARDY << 3) | (DRIVE_MODE << 4);
|
||||
}
|
||||
};
|
||||
meas_mode _meas_mode;
|
||||
|
||||
struct error_id {
|
||||
/* The CCS811 received an I²C write request addressed to this station but with
|
||||
invalid register address ID */
|
||||
uint8_t WRITE_REG_INVALID: 1;
|
||||
|
||||
/* The CCS811 received an I²C read request to a mailbox ID that is invalid */
|
||||
uint8_t READ_REG_INVALID: 1;
|
||||
|
||||
/* The CCS811 received an I²C request to write an unsupported mode to
|
||||
MEAS_MODE */
|
||||
uint8_t MEASMODE_INVALID: 1;
|
||||
|
||||
/* The sensor resistance measurement has reached or exceeded the maximum
|
||||
range */
|
||||
uint8_t MAX_RESISTANCE: 1;
|
||||
|
||||
/* The Heater current in the CCS811 is not in range */
|
||||
uint8_t HEATER_FAULT: 1;
|
||||
|
||||
/* The Heater voltage is not being applied correctly */
|
||||
uint8_t HEATER_SUPPLY: 1;
|
||||
|
||||
void set(uint8_t data){
|
||||
WRITE_REG_INVALID = data & 0x01;
|
||||
READ_REG_INVALID = (data & 0x02) >> 1;
|
||||
MEASMODE_INVALID = (data & 0x04) >> 2;
|
||||
MAX_RESISTANCE = (data & 0x08) >> 3;
|
||||
HEATER_FAULT = (data & 0x10) >> 4;
|
||||
HEATER_SUPPLY = (data & 0x20) >> 5;
|
||||
}
|
||||
};
|
||||
error_id _error_id;
|
||||
|
||||
/*=========================================================================*/
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,13 +0,0 @@
|
||||
# Adafruit CCS811 Library [](https://travis-ci.org/adafruit/Adafruit_CCS811)
|
||||
|
||||
<img src="https://cdn-shop.adafruit.com/970x728/3566-00.jpg" height="300"/>
|
||||
|
||||
This is a library for the Adafruit CCS811 gas sensor breakout board:
|
||||
* https://www.adafruit.com/product/3566
|
||||
|
||||
Check out the links above for our tutorials and wiring diagrams. This chip uses I2C to communicate
|
||||
|
||||
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
|
||||
|
||||
Written by Dean Miller for Adafruit Industries.
|
||||
MIT license, all text above must be included in any redistribution
|
||||
@@ -0,0 +1,289 @@
|
||||
#include "Adafruit_CCS811.h"
|
||||
|
||||
Adafruit_CCS811::~Adafruit_CCS811(void) {
|
||||
if (i2c_dev)
|
||||
delete i2c_dev;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Setups the I2C interface and hardware and checks for communication.
|
||||
@param addr Optional I2C address the sensor can be found on. Default is
|
||||
0x5A
|
||||
@param theWire Optional pointer to I2C interface, &Wire is used by default
|
||||
@returns True if device is set up, false on any failure
|
||||
*/
|
||||
/**************************************************************************/
|
||||
bool Adafruit_CCS811::begin(uint8_t addr, TwoWire *theWire) {
|
||||
if (i2c_dev)
|
||||
delete i2c_dev;
|
||||
i2c_dev = new Adafruit_I2CDevice(addr, theWire);
|
||||
if (!i2c_dev->begin()) {
|
||||
return false;
|
||||
}
|
||||
#ifdef ESP8266
|
||||
theWire->setClockStretchLimit(500);
|
||||
#endif
|
||||
|
||||
SWReset();
|
||||
delay(100);
|
||||
|
||||
// check that the HW id is correct
|
||||
if (this->read8(CCS811_HW_ID) != CCS811_HW_ID_CODE)
|
||||
return false;
|
||||
|
||||
// try to start the app
|
||||
this->write(CCS811_BOOTLOADER_APP_START, NULL, 0);
|
||||
delay(100);
|
||||
|
||||
// make sure there are no errors and we have entered application mode
|
||||
if (checkError())
|
||||
return false;
|
||||
if (!_status.FW_MODE)
|
||||
return false;
|
||||
|
||||
disableInterrupt();
|
||||
|
||||
// default to read every second
|
||||
setDriveMode(CCS811_DRIVE_MODE_1SEC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief sample rate of the sensor.
|
||||
@param mode one of CCS811_DRIVE_MODE_IDLE, CCS811_DRIVE_MODE_1SEC,
|
||||
CCS811_DRIVE_MODE_10SEC, CCS811_DRIVE_MODE_60SEC, CCS811_DRIVE_MODE_250MS.
|
||||
*/
|
||||
void Adafruit_CCS811::setDriveMode(uint8_t mode) {
|
||||
_meas_mode.DRIVE_MODE = mode;
|
||||
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief enable the data ready interrupt pin on the device.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::enableInterrupt() {
|
||||
_meas_mode.INT_DATARDY = 1;
|
||||
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief disable the data ready interrupt pin on the device
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::disableInterrupt() {
|
||||
_meas_mode.INT_DATARDY = 0;
|
||||
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief checks if data is available to be read.
|
||||
@returns True if data is ready, false otherwise.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
bool Adafruit_CCS811::available() {
|
||||
_status.set(read8(CCS811_STATUS));
|
||||
if (!_status.DATA_READY)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief read and store the sensor data. This data can be accessed with
|
||||
getTVOC(), geteCO2(), getCurrentSelected() and getRawADCreading()
|
||||
@returns 0 if no error, error code otherwise.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint8_t Adafruit_CCS811::readData() {
|
||||
if (!available())
|
||||
return false;
|
||||
else {
|
||||
uint8_t buf[8];
|
||||
this->read(CCS811_ALG_RESULT_DATA, buf, 8);
|
||||
|
||||
_eCO2 = ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
|
||||
_TVOC = ((uint16_t)buf[2] << 8) | ((uint16_t)buf[3]);
|
||||
_currentSelected = ((uint16_t)buf[6] >> 2);
|
||||
_rawADCreading = ((uint16_t)(buf[6] & 3) << 8) | ((uint16_t)buf[7]);
|
||||
|
||||
if (_status.ERROR)
|
||||
return buf[5];
|
||||
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief set the humidity and temperature compensation for the sensor.
|
||||
@param humidity the humidity data as a percentage. For 55.5% humidity, pass
|
||||
in 55.5
|
||||
@param temperature the temperature in degrees C as a decimal number.
|
||||
For 25.5 degrees C, pass in 25.5
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::setEnvironmentalData(float humidity, float temperature) {
|
||||
/* Humidity is stored as an unsigned 16 bits in 1/512%RH. The
|
||||
default value is 50% = 0x64, 0x00. As an example 48.5%
|
||||
humidity would be 0x61, 0x00.*/
|
||||
|
||||
/* Temperature is stored as an unsigned 16 bits integer in 1/512
|
||||
degrees; there is an offset: 0 maps to -25°C. The default value is
|
||||
25°C = 0x64, 0x00. As an example 23.5% temperature would be
|
||||
0x61, 0x00.
|
||||
The internal algorithm uses these values (or default values if
|
||||
not set by the application) to compensate for changes in
|
||||
relative humidity and ambient temperature.*/
|
||||
|
||||
uint16_t hum_conv = humidity * 512.0f + 0.5f;
|
||||
uint16_t temp_conv = (temperature + 25.0f) * 512.0f + 0.5f;
|
||||
|
||||
uint8_t buf[] = {
|
||||
(uint8_t)((hum_conv >> 8) & 0xFF), (uint8_t)(hum_conv & 0xFF),
|
||||
(uint8_t)((temp_conv >> 8) & 0xFF), (uint8_t)(temp_conv & 0xFF)};
|
||||
|
||||
this->write(CCS811_ENV_DATA, buf, 4);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief get the current baseline from the sensor.
|
||||
@returns the baseline as 16 bit integer. This value is not human readable.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint16_t Adafruit_CCS811::getBaseline() {
|
||||
/* baseline is not in a human readable format, the two bytes are assembled
|
||||
to an uint16_t for easy handling/passing around */
|
||||
|
||||
uint8_t buf[2];
|
||||
|
||||
this->read(CCS811_BASELINE, buf, 2);
|
||||
|
||||
return ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief set the baseline for the sensor.
|
||||
@param baseline the baseline to be set. Has to be a value retrieved by
|
||||
getBaseline().
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::setBaseline(uint16_t baseline) {
|
||||
/* baseline is not in a human readable format, byte ordering matches
|
||||
getBaseline() */
|
||||
|
||||
uint8_t buf[] = {(uint8_t)((baseline >> 8) & 0xFF),
|
||||
(uint8_t)(baseline & 0xFF)};
|
||||
|
||||
this->write(CCS811_BASELINE, buf, 2);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@deprecated hardware support removed by vendor
|
||||
@brief calculate the temperature using the onboard NTC resistor.
|
||||
@returns temperature as a double.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
double Adafruit_CCS811::calculateTemperature() {
|
||||
uint8_t buf[4];
|
||||
this->read(CCS811_NTC, buf, 4);
|
||||
|
||||
uint32_t vref = ((uint32_t)buf[0] << 8) | buf[1];
|
||||
uint32_t vntc = ((uint32_t)buf[2] << 8) | buf[3];
|
||||
|
||||
// from ams ccs811 app note
|
||||
uint32_t rntc = vntc * CCS811_REF_RESISTOR / vref;
|
||||
|
||||
double ntc_temp;
|
||||
ntc_temp = log((double)rntc / CCS811_REF_RESISTOR); // 1
|
||||
ntc_temp /= 3380; // 2
|
||||
ntc_temp += 1.0 / (25 + 273.15); // 3
|
||||
ntc_temp = 1.0 / ntc_temp; // 4
|
||||
ntc_temp -= 273.15; // 5
|
||||
return ntc_temp - _tempOffset;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief set interrupt thresholds
|
||||
@param low_med the level below which an interrupt will be triggered.
|
||||
@param med_high the level above which the interrupt will ge triggered.
|
||||
@param hysteresis optional histeresis level. Defaults to 50
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::setThresholds(uint16_t low_med, uint16_t med_high,
|
||||
uint8_t hysteresis) {
|
||||
uint8_t buf[] = {(uint8_t)((low_med >> 8) & 0xF), (uint8_t)(low_med & 0xF),
|
||||
(uint8_t)((med_high >> 8) & 0xF), (uint8_t)(med_high & 0xF),
|
||||
hysteresis};
|
||||
|
||||
this->write(CCS811_THRESHOLDS, buf, 5);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief trigger a software reset of the device
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::SWReset() {
|
||||
// reset sequence from the datasheet
|
||||
uint8_t seq[] = {0x11, 0xE5, 0x72, 0x8A};
|
||||
this->write(CCS811_SW_RESET, seq, 4);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief read the status register and store any errors.
|
||||
@returns the error bits from the status register of the device.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
bool Adafruit_CCS811::checkError() {
|
||||
_status.set(read8(CCS811_STATUS));
|
||||
return _status.ERROR;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief write one byte of data to the specified register
|
||||
@param reg the register to write to
|
||||
@param value the value to write
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Adafruit_CCS811::write8(byte reg, byte value) {
|
||||
this->write(reg, &value, 1);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief read one byte of data from the specified register
|
||||
@param reg the register to read
|
||||
@returns one byte of register data
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint8_t Adafruit_CCS811::read8(byte reg) {
|
||||
uint8_t ret;
|
||||
this->read(reg, &ret, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Adafruit_CCS811::read(uint8_t reg, uint8_t *buf, uint8_t num) {
|
||||
uint8_t buffer[1] = {reg};
|
||||
i2c_dev->write_then_read(buffer, 1, buf, num);
|
||||
}
|
||||
|
||||
void Adafruit_CCS811::write(uint8_t reg, uint8_t *buf, uint8_t num) {
|
||||
uint8_t prefix[1] = {reg};
|
||||
i2c_dev->write(buf, num, true, prefix, 1);
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
#ifndef LIB_ADAFRUIT_CCS811_H
|
||||
#define LIB_ADAFRUIT_CCS811_H
|
||||
|
||||
#if (ARDUINO >= 100)
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <Adafruit_I2CDevice.h>
|
||||
|
||||
/*=========================================================================
|
||||
I2C ADDRESS/BITS
|
||||
-----------------------------------------------------------------------*/
|
||||
#define CCS811_ADDRESS (0x5A)
|
||||
/*=========================================================================*/
|
||||
|
||||
/*=========================================================================
|
||||
REGISTERS
|
||||
-----------------------------------------------------------------------*/
|
||||
enum {
|
||||
CCS811_STATUS = 0x00,
|
||||
CCS811_MEAS_MODE = 0x01,
|
||||
CCS811_ALG_RESULT_DATA = 0x02,
|
||||
CCS811_RAW_DATA = 0x03,
|
||||
CCS811_ENV_DATA = 0x05,
|
||||
CCS811_NTC = 0x06,
|
||||
CCS811_THRESHOLDS = 0x10,
|
||||
CCS811_BASELINE = 0x11,
|
||||
CCS811_HW_ID = 0x20,
|
||||
CCS811_HW_VERSION = 0x21,
|
||||
CCS811_FW_BOOT_VERSION = 0x23,
|
||||
CCS811_FW_APP_VERSION = 0x24,
|
||||
CCS811_ERROR_ID = 0xE0,
|
||||
CCS811_SW_RESET = 0xFF,
|
||||
};
|
||||
|
||||
// bootloader registers
|
||||
enum {
|
||||
CCS811_BOOTLOADER_APP_ERASE = 0xF1,
|
||||
CCS811_BOOTLOADER_APP_DATA = 0xF2,
|
||||
CCS811_BOOTLOADER_APP_VERIFY = 0xF3,
|
||||
CCS811_BOOTLOADER_APP_START = 0xF4
|
||||
};
|
||||
|
||||
enum {
|
||||
CCS811_DRIVE_MODE_IDLE = 0x00,
|
||||
CCS811_DRIVE_MODE_1SEC = 0x01,
|
||||
CCS811_DRIVE_MODE_10SEC = 0x02,
|
||||
CCS811_DRIVE_MODE_60SEC = 0x03,
|
||||
CCS811_DRIVE_MODE_250MS = 0x04,
|
||||
};
|
||||
|
||||
/*=========================================================================*/
|
||||
|
||||
#define CCS811_HW_ID_CODE 0x81
|
||||
|
||||
#define CCS811_REF_RESISTOR 100000
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Class that stores state and functions for interacting with CCS811
|
||||
gas sensor chips
|
||||
*/
|
||||
/**************************************************************************/
|
||||
class Adafruit_CCS811 {
|
||||
public:
|
||||
// constructors
|
||||
Adafruit_CCS811(void){};
|
||||
~Adafruit_CCS811(void);
|
||||
|
||||
bool begin(uint8_t addr = CCS811_ADDRESS, TwoWire *theWire = &Wire);
|
||||
|
||||
void setEnvironmentalData(float humidity, float temperature);
|
||||
|
||||
uint16_t getBaseline();
|
||||
void setBaseline(uint16_t baseline);
|
||||
|
||||
// calculate temperature based on the NTC register
|
||||
double calculateTemperature();
|
||||
|
||||
void setThresholds(uint16_t low_med, uint16_t med_high,
|
||||
uint8_t hysteresis = 50);
|
||||
|
||||
void SWReset();
|
||||
|
||||
void setDriveMode(uint8_t mode);
|
||||
void enableInterrupt();
|
||||
void disableInterrupt();
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief returns the stored total volatile organic compounds measurement.
|
||||
This does does not read the sensor. To do so, call readData()
|
||||
@returns TVOC measurement as 16 bit integer
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint16_t getTVOC() { return _TVOC; }
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief returns the stored estimated carbon dioxide measurement. This does
|
||||
does not read the sensor. To do so, call readData()
|
||||
@returns eCO2 measurement as 16 bit integer
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint16_t geteCO2() { return _eCO2; }
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief returns the "Current Selected" in uA.
|
||||
This does does not read the sensor. To do so, call readData()
|
||||
@returns "Current Selected" in uA as 16 bit integer
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint16_t getCurrentSelected() { return _currentSelected; }
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief returns the raw ADC reading. This does
|
||||
does not read the sensor. To do so, call readData()
|
||||
@returns raw ADC reading as 16 bit integer
|
||||
*/
|
||||
/**************************************************************************/
|
||||
uint16_t getRawADCreading() { return _rawADCreading; }
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief set the temperature compensation offset for the device. This is
|
||||
needed to offset errors in NTC measurements.
|
||||
@param offset the offset to be added to temperature measurements.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void setTempOffset(float offset) { _tempOffset = offset; }
|
||||
|
||||
// check if data is available to be read
|
||||
bool available();
|
||||
uint8_t readData();
|
||||
|
||||
bool checkError();
|
||||
|
||||
private:
|
||||
Adafruit_I2CDevice *i2c_dev = NULL; ///< Pointer to I2C bus interface
|
||||
float _tempOffset;
|
||||
|
||||
uint16_t _TVOC;
|
||||
uint16_t _eCO2;
|
||||
|
||||
uint16_t _currentSelected;
|
||||
uint16_t _rawADCreading;
|
||||
|
||||
void write8(byte reg, byte value);
|
||||
void write16(byte reg, uint16_t value);
|
||||
uint8_t read8(byte reg);
|
||||
|
||||
void read(uint8_t reg, uint8_t *buf, uint8_t num);
|
||||
void write(uint8_t reg, uint8_t *buf, uint8_t num);
|
||||
|
||||
/*=========================================================================
|
||||
REGISTER BITFIELDS
|
||||
-----------------------------------------------------------------------*/
|
||||
// The status register
|
||||
struct status {
|
||||
|
||||
/* 0: no error
|
||||
* 1: error has occurred
|
||||
*/
|
||||
uint8_t ERROR : 1;
|
||||
|
||||
// reserved : 2
|
||||
|
||||
/* 0: no samples are ready
|
||||
* 1: samples are ready
|
||||
*/
|
||||
uint8_t DATA_READY : 1;
|
||||
uint8_t APP_VALID : 1;
|
||||
|
||||
// reserved : 2
|
||||
|
||||
/* 0: boot mode, new firmware can be loaded
|
||||
* 1: application mode, can take measurements
|
||||
*/
|
||||
uint8_t FW_MODE : 1;
|
||||
|
||||
void set(uint8_t data) {
|
||||
ERROR = data & 0x01;
|
||||
DATA_READY = (data >> 3) & 0x01;
|
||||
APP_VALID = (data >> 4) & 0x01;
|
||||
FW_MODE = (data >> 7) & 0x01;
|
||||
}
|
||||
};
|
||||
status _status;
|
||||
|
||||
// measurement and conditions register
|
||||
struct meas_mode {
|
||||
// reserved : 2
|
||||
|
||||
/* 0: interrupt mode operates normally
|
||||
* 1: Interrupt mode (if enabled) only asserts the nINT signal (driven low) if
|
||||
the new ALG_RESULT_DATA crosses one of the thresholds set in the THRESHOLDS
|
||||
register by more than the hysteresis value (also in the THRESHOLDS register)
|
||||
*/
|
||||
uint8_t INT_THRESH : 1;
|
||||
|
||||
/* 0: int disabled
|
||||
* 1: The nINT signal is asserted (driven low) when a new sample is ready in
|
||||
ALG_RESULT_DATA. The nINT signal will stop being driven low
|
||||
when ALG_RESULT_DATA is read on the I²C interface.
|
||||
*/
|
||||
uint8_t INT_DATARDY : 1;
|
||||
|
||||
uint8_t DRIVE_MODE : 3;
|
||||
|
||||
uint8_t get() {
|
||||
return (INT_THRESH << 2) | (INT_DATARDY << 3) | (DRIVE_MODE << 4);
|
||||
}
|
||||
};
|
||||
meas_mode _meas_mode;
|
||||
|
||||
struct error_id {
|
||||
/* The CCS811 received an I²C write request addressed to this station but
|
||||
with invalid register address ID */
|
||||
uint8_t WRITE_REG_INVALID : 1;
|
||||
|
||||
/* The CCS811 received an I²C read request to a mailbox ID that is invalid
|
||||
*/
|
||||
uint8_t READ_REG_INVALID : 1;
|
||||
|
||||
/* The CCS811 received an I²C request to write an unsupported mode to
|
||||
MEAS_MODE */
|
||||
uint8_t MEASMODE_INVALID : 1;
|
||||
|
||||
/* The sensor resistance measurement has reached or exceeded the maximum
|
||||
range */
|
||||
uint8_t MAX_RESISTANCE : 1;
|
||||
|
||||
/* The Heater current in the CCS811 is not in range */
|
||||
uint8_t HEATER_FAULT : 1;
|
||||
|
||||
/* The Heater voltage is not being applied correctly */
|
||||
uint8_t HEATER_SUPPLY : 1;
|
||||
|
||||
void set(uint8_t data) {
|
||||
WRITE_REG_INVALID = data & 0x01;
|
||||
READ_REG_INVALID = (data & 0x02) >> 1;
|
||||
MEASMODE_INVALID = (data & 0x04) >> 2;
|
||||
MAX_RESISTANCE = (data & 0x08) >> 3;
|
||||
HEATER_FAULT = (data & 0x10) >> 4;
|
||||
HEATER_SUPPLY = (data & 0x20) >> 5;
|
||||
}
|
||||
};
|
||||
error_id _error_id;
|
||||
|
||||
/*=========================================================================*/
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
# Adafruit CCS811 [](https://github.com/adafruit/Adafruit_CCS811/actions)[](http://adafruit.github.io/Adafruit_CCS811/html/index.html)
|
||||
|
||||
|
||||
<img src="https://cdn-shop.adafruit.com/970x728/3566-05.jpg" height="300"/>
|
||||
|
||||
This is a library for the Adafruit CCS811 gas sensor breakout board:
|
||||
* https://www.adafruit.com/product/3566
|
||||
|
||||
Check out the [product guide on the Adafruit Learning System](https://learn.adafruit.com/adafruit-ccs811-air-quality-sensor) for our tutorials and wiring diagrams.
|
||||
This chip uses **I2C** to communicate
|
||||
|
||||
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
|
||||
# Dependencies
|
||||
* [Adafruit SSD1306](https://github.com/adafruit/Adafruit SSD1306)
|
||||
* [Adafruit GFX Library](https://github.com/adafruit/Adafruit-GFX-Library)
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions are welcome! Please read our [Code of Conduct](https://github.com/adafruit/Adafruit_CCS811/blob/master/CODE_OF_CONDUCT.md>)
|
||||
before contributing to help this project stay welcoming.
|
||||
|
||||
## Documentation and doxygen
|
||||
Documentation is produced by doxygen. Contributions should include documentation for any new code added.
|
||||
|
||||
Some examples of how to use doxygen can be found in these guide pages:
|
||||
|
||||
https://learn.adafruit.com/the-well-automated-arduino-library/doxygen
|
||||
|
||||
https://learn.adafruit.com/the-well-automated-arduino-library/doxygen-tips
|
||||
|
||||
|
||||
Written by Dean Miller for Adafruit Industries.
|
||||
MIT license, all text above must be included in any redistribution. See license.txt for more information
|
||||
All text above must be included in any redistribution
|
||||
|
||||
To install, use the Arduino Library Manager and search for "Adafruit CCS811" and install the library.
|
||||
+6
-11
@@ -1,5 +1,5 @@
|
||||
/***************************************************************************
|
||||
This is a library for the CCS811 air
|
||||
This is a library for the CCS811 air
|
||||
|
||||
This sketch reads the sensor
|
||||
|
||||
@@ -22,30 +22,25 @@ Adafruit_CCS811 ccs;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
|
||||
Serial.println("CCS811 test");
|
||||
|
||||
|
||||
if(!ccs.begin()){
|
||||
Serial.println("Failed to start sensor! Please check your wiring.");
|
||||
while(1);
|
||||
}
|
||||
|
||||
//calibrate temperature sensor
|
||||
// Wait for the sensor to be ready
|
||||
while(!ccs.available());
|
||||
float temp = ccs.calculateTemperature();
|
||||
ccs.setTempOffset(temp - 25.0);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if(ccs.available()){
|
||||
float temp = ccs.calculateTemperature();
|
||||
if(!ccs.readData()){
|
||||
Serial.print("CO2: ");
|
||||
Serial.print(ccs.geteCO2());
|
||||
Serial.print("ppm, TVOC: ");
|
||||
Serial.print(ccs.getTVOC());
|
||||
Serial.print("ppb Temp:");
|
||||
Serial.println(temp);
|
||||
Serial.println(ccs.getTVOC());
|
||||
}
|
||||
else{
|
||||
Serial.println("ERROR!");
|
||||
@@ -53,4 +48,4 @@ void loop() {
|
||||
}
|
||||
}
|
||||
delay(500);
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
name=Adafruit CCS811 Library
|
||||
version=1.0.0
|
||||
version=1.1.3
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=This is a library for the Adafruit CCS811 I2C gas sensor breakout.
|
||||
paragraph=This is a library for the Adafruit CCS811 I2C gas sensor breakou.
|
||||
paragraph=CCS811 is a gas sensor that can detect a wide range of Volatile Organic Compounds (VOCs) and is intended for indoor air quality monitoring.
|
||||
category=Sensors
|
||||
url=https://github.com/adafruit/Adafruit_CCS811
|
||||
architectures=*
|
||||
depends=Adafruit SSD1306, Adafruit GFX Library, Adafruit BusIO
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2020 Dean Miller for Adafruit Industries
|
||||
|
||||
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.
|
||||
@@ -1,27 +0,0 @@
|
||||
language: c
|
||||
sudo: false
|
||||
|
||||
# Blacklist
|
||||
branches:
|
||||
except:
|
||||
- gh-pages
|
||||
|
||||
env:
|
||||
global:
|
||||
- PRETTYNAME="Adafruit SGP30 Arduino Library"
|
||||
# Optional, will default to "$TRAVIS_BUILD_DIR/Doxyfile"
|
||||
# - DOXYFILE: $TRAVIS_BUILD_DIR/Doxyfile
|
||||
|
||||
before_install:
|
||||
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh)
|
||||
|
||||
#install:
|
||||
# - arduino --install-library "Adafruit ILI9341","Adafruit GFX Library"
|
||||
|
||||
script:
|
||||
- build_main_platforms
|
||||
|
||||
# Generate and deploy documentation
|
||||
after_success:
|
||||
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/library_check.sh)
|
||||
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/doxy_gen_and_deploy.sh)
|
||||
+18
-44
@@ -25,10 +25,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "Adafruit_SGP30.h"
|
||||
//#define I2C_DEBUG
|
||||
#include "Arduino.h"
|
||||
|
||||
/*!
|
||||
* @brief Instantiates a new SGP30 class
|
||||
@@ -47,10 +45,15 @@ Adafruit_SGP30::Adafruit_SGP30() {}
|
||||
* @return True if SGP30 found on I2C, False if something went wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP30::begin(TwoWire *theWire, boolean initSensor) {
|
||||
_i2caddr = SGP30_I2CADDR_DEFAULT;
|
||||
_i2c = theWire;
|
||||
if (i2c_dev) {
|
||||
delete i2c_dev; // remove old interface
|
||||
}
|
||||
|
||||
_i2c->begin();
|
||||
i2c_dev = new Adafruit_I2CDevice(SGP30_I2CADDR_DEFAULT, theWire);
|
||||
|
||||
if (!i2c_dev->begin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t command[2];
|
||||
command[0] = 0x36;
|
||||
@@ -217,30 +220,14 @@ boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) {
|
||||
* @brief I2C low level interfacing
|
||||
*/
|
||||
|
||||
boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[],
|
||||
uint8_t commandLength,
|
||||
uint16_t delayms,
|
||||
uint16_t *readdata,
|
||||
uint8_t readlen) {
|
||||
bool Adafruit_SGP30::readWordFromCommand(uint8_t command[],
|
||||
uint8_t commandLength,
|
||||
uint16_t delayms, uint16_t *readdata,
|
||||
uint8_t readlen) {
|
||||
|
||||
_i2c->beginTransmission(_i2caddr);
|
||||
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\t-> ");
|
||||
#endif
|
||||
|
||||
for (uint8_t i = 0; i < commandLength; i++) {
|
||||
_i2c->write(command[i]);
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("0x");
|
||||
Serial.print(command[i], HEX);
|
||||
Serial.print(", ");
|
||||
#endif
|
||||
if (!i2c_dev->write(command, commandLength)) {
|
||||
return false;
|
||||
}
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.println();
|
||||
#endif
|
||||
_i2c->endTransmission();
|
||||
|
||||
delay(delayms);
|
||||
|
||||
@@ -248,24 +235,11 @@ boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[],
|
||||
return true;
|
||||
|
||||
uint8_t replylen = readlen * (SGP30_WORD_LEN + 1);
|
||||
if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
|
||||
return false;
|
||||
uint8_t replybuffer[replylen];
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("\t\t<- ");
|
||||
#endif
|
||||
for (uint8_t i = 0; i < replylen; i++) {
|
||||
replybuffer[i] = _i2c->read();
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.print("0x");
|
||||
Serial.print(replybuffer[i], HEX);
|
||||
Serial.print(", ");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef I2C_DEBUG
|
||||
Serial.println();
|
||||
#endif
|
||||
if (!i2c_dev->read(replybuffer, replylen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < readlen; i++) {
|
||||
uint8_t crc = generateCRC(replybuffer + i * 3, 2);
|
||||
+14
-11
@@ -18,8 +18,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ADAFRUIT_SGP30_H
|
||||
#define ADAFRUIT_SGP30_H
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_BusIO_Register.h>
|
||||
#include <Adafruit_I2CDevice.h>
|
||||
|
||||
// the i2c address
|
||||
#define SGP30_I2CADDR_DEFAULT 0x58 ///< SGP30 has only one I2C address
|
||||
@@ -55,12 +59,12 @@ public:
|
||||
* value is set when you call {@link IAQmeasure()} **/
|
||||
uint16_t eCO2;
|
||||
|
||||
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
|
||||
* value is set when you call {@link IAQmeasureRaw()} **/
|
||||
/** The last measurement of the raw H2 signal. This value is set when you call
|
||||
* {@link IAQmeasureRaw()} **/
|
||||
uint16_t rawH2;
|
||||
|
||||
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
|
||||
* value is set when you call {@link IAQmeasureRaw()} **/
|
||||
/** The last measurement of the raw ethanol signal. This value is set when you
|
||||
* call {@link IAQmeasureRaw()} **/
|
||||
uint16_t rawEthanol;
|
||||
|
||||
/** The 48-bit serial number, this value is set when you call {@link begin()}
|
||||
@@ -68,13 +72,12 @@ public:
|
||||
uint16_t serialnumber[3];
|
||||
|
||||
private:
|
||||
TwoWire *_i2c;
|
||||
uint8_t _i2caddr;
|
||||
|
||||
Adafruit_I2CDevice *i2c_dev = NULL; ///< Pointer to I2C bus interface
|
||||
void write(uint8_t address, uint8_t *data, uint8_t n);
|
||||
void read(uint8_t address, uint8_t *data, uint8_t n);
|
||||
boolean readWordFromCommand(uint8_t command[], uint8_t commandLength,
|
||||
uint16_t delay, uint16_t *readdata = NULL,
|
||||
uint8_t readlen = 0);
|
||||
bool readWordFromCommand(uint8_t command[], uint8_t commandLength,
|
||||
uint16_t delay, uint16_t *readdata = NULL,
|
||||
uint8_t readlen = 0);
|
||||
uint8_t generateCRC(uint8_t data[], uint8_t datalen);
|
||||
};
|
||||
#endif // ndef ADAFRUIT_SGP30_H
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
# Adafruit SGP30 Gas / Air Quality I2C sensor [[](https://github.com/adafruit/Adafruit_SGP30/actions)[](http://adafruit.github.io/Adafruit_SGP30/html/index.html)
|
||||
# Adafruit SGP30 Gas / Air Quality I2C sensor [](https://github.com/adafruit/Adafruit_SGP30/actions)[](http://adafruit.github.io/Adafruit_SGP30/html/index.html)
|
||||
|
||||
<a href="https://www.adafruit.com/product/3709"><img src="assets/board.jpg?raw=true" width="500px"></a>
|
||||
|
||||
@@ -15,8 +15,7 @@ Adafruit invests time and resources providing this open source code, please supp
|
||||
To install, use the Arduino Library Manager and search for "Adafruit SGP30" and install the library.
|
||||
|
||||
## Dependencies
|
||||
* [Adafruit ILI9341](https://github.com/adafruit/Adafruit_ILI9341)
|
||||
* [Adafruit GFX Library](https://github.com/adafruit/Adafruit-GFX-Library)
|
||||
* [Adafruit Bus IO](https://github.com/adafruit/Adafruit_BusIO)
|
||||
|
||||
# Contributing
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 353 KiB |
@@ -0,0 +1,127 @@
|
||||
# Adafruit Community Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and leaders pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level or type of
|
||||
experience, education, socio-economic status, nationality, personal appearance,
|
||||
race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
We are committed to providing a friendly, safe and welcoming environment for
|
||||
all.
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Be kind and courteous to others
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Collaborating with other community members
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and sexual attention or advances
|
||||
* The use of inappropriate images, including in a community member's avatar
|
||||
* The use of inappropriate language, including in a community member's nickname
|
||||
* Any spamming, flaming, baiting or other attention-stealing behavior
|
||||
* Excessive or unwelcome helping; answering outside the scope of the question
|
||||
asked
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate
|
||||
|
||||
The goal of the standards and moderation guidelines outlined here is to build
|
||||
and maintain a respectful community. We ask that you don’t just aim to be
|
||||
"technically unimpeachable", but rather try to be your best self.
|
||||
|
||||
We value many things beyond technical expertise, including collaboration and
|
||||
supporting others within our community. Providing a positive experience for
|
||||
other community members can have a much more significant impact than simply
|
||||
providing the correct answer.
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project leaders are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project leaders have the right and responsibility to remove, edit, or
|
||||
reject messages, comments, commits, code, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any community member for other behaviors that they deem
|
||||
inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Moderation
|
||||
|
||||
Instances of behaviors that violate the Adafruit Community Code of Conduct
|
||||
may be reported by any member of the community. Community members are
|
||||
encouraged to report these situations, including situations they witness
|
||||
involving other community members.
|
||||
|
||||
You may report in the following ways:
|
||||
|
||||
In any situation, you may send an email to <support@adafruit.com>.
|
||||
|
||||
On the Adafruit Discord, you may send an open message from any channel
|
||||
to all Community Helpers by tagging @community helpers. You may also send an
|
||||
open message from any channel, or a direct message to @kattni#1507,
|
||||
@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or
|
||||
@Andon#8175.
|
||||
|
||||
Email and direct message reports will be kept confidential.
|
||||
|
||||
In situations on Discord where the issue is particularly egregious, possibly
|
||||
illegal, requires immediate action, or violates the Discord terms of service,
|
||||
you should also report the message directly to Discord.
|
||||
|
||||
These are the steps for upholding our community’s standards of conduct.
|
||||
|
||||
1. Any member of the community may report any situation that violates the
|
||||
Adafruit Community Code of Conduct. All reports will be reviewed and
|
||||
investigated.
|
||||
2. If the behavior is an egregious violation, the community member who
|
||||
committed the violation may be banned immediately, without warning.
|
||||
3. Otherwise, moderators will first respond to such behavior with a warning.
|
||||
4. Moderators follow a soft "three strikes" policy - the community member may
|
||||
be given another chance, if they are receptive to the warning and change their
|
||||
behavior.
|
||||
5. If the community member is unreceptive or unreasonable when warned by a
|
||||
moderator, or the warning goes unheeded, they may be banned for a first or
|
||||
second offense. Repeated offenses will result in the community member being
|
||||
banned.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct and the enforcement policies listed above apply to all
|
||||
Adafruit Community venues. This includes but is not limited to any community
|
||||
spaces (both public and private), the entire Adafruit Discord server, and
|
||||
Adafruit GitHub repositories. Examples of Adafruit Community spaces include
|
||||
but are not limited to meet-ups, audio chats on the Adafruit Discord, or
|
||||
interaction at a conference.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. As a community
|
||||
member, you are representing our community, and are expected to behave
|
||||
accordingly.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
<https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>,
|
||||
and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html).
|
||||
|
||||
For other projects adopting the Adafruit Community Code of
|
||||
Conduct, please contact the maintainers of those projects for enforcement.
|
||||
If you wish to use this code of conduct for your own project, consider
|
||||
explicitly mentioning your moderation policy or making a copy with your
|
||||
own moderation policy so as to avoid confusion.
|
||||
+3
-1
@@ -15,7 +15,9 @@ uint32_t getAbsoluteHumidity(float temperature, float humidity) {
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.begin(115200);
|
||||
while (!Serial) { delay(10); } // Wait for serial console to open!
|
||||
|
||||
Serial.println("SGP30 test");
|
||||
|
||||
if (! sgp.begin()){
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
name=Adafruit SGP30 Sensor
|
||||
version=1.2.0
|
||||
version=2.0.3
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor
|
||||
@@ -7,4 +7,4 @@ paragraph=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Se
|
||||
category=Sensors
|
||||
url=https://github.com/adafruit/Adafruit_SGP30
|
||||
architectures=*
|
||||
depends=Adafruit ILI9341, Adafruit GFX Library
|
||||
depends=Adafruit BusIO
|
||||
@@ -1,805 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Sensirion AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Sensirion AG nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "sensirion_voc_algorithm.h"
|
||||
|
||||
/* The fixed point arithmetic parts of this code were originally created by
|
||||
* https://github.com/PetteriAimonen/libfixmath
|
||||
*/
|
||||
|
||||
/*!< the maximum value of fix16_t */
|
||||
#define FIX16_MAXIMUM 0x7FFFFFFF
|
||||
/*!< the minimum value of fix16_t */
|
||||
#define FIX16_MINIMUM 0x80000000
|
||||
/*!< the value used to indicate overflows when FIXMATH_NO_OVERFLOW is not
|
||||
* specified */
|
||||
#define FIX16_OVERFLOW 0x80000000
|
||||
/*!< fix16_t value of 1 */
|
||||
#define FIX16_ONE 0x00010000
|
||||
|
||||
static inline fix16_t fix16_from_int(int32_t a) {
|
||||
return a * FIX16_ONE;
|
||||
}
|
||||
|
||||
static inline int32_t fix16_cast_to_int(fix16_t a) {
|
||||
return (a >= 0) ? (a >> 16) : -((-a) >> 16);
|
||||
}
|
||||
|
||||
/*! Multiplies the two given fix16_t's and returns the result. */
|
||||
static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1);
|
||||
|
||||
/*! Divides the first given fix16_t by the second and returns the result. */
|
||||
static fix16_t fix16_div(fix16_t inArg0, fix16_t inArg1);
|
||||
|
||||
/*! Returns the square root of the given fix16_t. */
|
||||
static fix16_t fix16_sqrt(fix16_t inValue);
|
||||
|
||||
/*! Returns the exponent (e^) of the given fix16_t. */
|
||||
static fix16_t fix16_exp(fix16_t inValue);
|
||||
|
||||
static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) {
|
||||
// Each argument is divided to 16-bit parts.
|
||||
// AB
|
||||
// * CD
|
||||
// -----------
|
||||
// BD 16 * 16 -> 32 bit products
|
||||
// CB
|
||||
// AD
|
||||
// AC
|
||||
// |----| 64 bit product
|
||||
uint32_t absArg0 = (uint32_t)((inArg0 >= 0) ? inArg0 : (-inArg0));
|
||||
uint32_t absArg1 = (uint32_t)((inArg1 >= 0) ? inArg1 : (-inArg1));
|
||||
uint32_t A = (absArg0 >> 16), C = (absArg1 >> 16);
|
||||
uint32_t B = (absArg0 & 0xFFFF), D = (absArg1 & 0xFFFF);
|
||||
|
||||
uint32_t AC = A * C;
|
||||
uint32_t AD_CB = A * D + C * B;
|
||||
uint32_t BD = B * D;
|
||||
|
||||
uint32_t product_hi = AC + (AD_CB >> 16);
|
||||
|
||||
// Handle carry from lower 32 bits to upper part of result.
|
||||
uint32_t ad_cb_temp = AD_CB << 16;
|
||||
uint32_t product_lo = BD + ad_cb_temp;
|
||||
if (product_lo < BD)
|
||||
product_hi++;
|
||||
|
||||
#ifndef FIXMATH_NO_OVERFLOW
|
||||
// The upper 17 bits should all be zero.
|
||||
if (product_hi >> 15)
|
||||
return (fix16_t)FIX16_OVERFLOW;
|
||||
#endif
|
||||
|
||||
#ifdef FIXMATH_NO_ROUNDING
|
||||
fix16_t result = (fix16_t)((product_hi << 16) | (product_lo >> 16));
|
||||
if ((inArg0 < 0) != (inArg1 < 0))
|
||||
result = -result;
|
||||
return result;
|
||||
#else
|
||||
// Adding 0x8000 (= 0.5) and then using right shift
|
||||
// achieves proper rounding to result.
|
||||
// Handle carry from lower to upper part.
|
||||
uint32_t product_lo_tmp = product_lo;
|
||||
product_lo += 0x8000;
|
||||
if (product_lo < product_lo_tmp)
|
||||
product_hi++;
|
||||
|
||||
// Discard the lowest 16 bits and convert back to signed result.
|
||||
fix16_t result = (fix16_t)((product_hi << 16) | (product_lo >> 16));
|
||||
if ((inArg0 < 0) != (inArg1 < 0))
|
||||
result = -result;
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
static fix16_t fix16_div(fix16_t a, fix16_t b) {
|
||||
// This uses the basic binary restoring division algorithm.
|
||||
// It appears to be faster to do the whole division manually than
|
||||
// trying to compose a 64-bit divide out of 32-bit divisions on
|
||||
// platforms without hardware divide.
|
||||
|
||||
if (b == 0)
|
||||
return (fix16_t)FIX16_MINIMUM;
|
||||
|
||||
uint32_t remainder = (uint32_t)((a >= 0) ? a : (-a));
|
||||
uint32_t divider = (uint32_t)((b >= 0) ? b : (-b));
|
||||
|
||||
uint32_t quotient = 0;
|
||||
uint32_t bit = 0x10000;
|
||||
|
||||
/* The algorithm requires D >= R */
|
||||
while (divider < remainder) {
|
||||
divider <<= 1;
|
||||
bit <<= 1;
|
||||
}
|
||||
|
||||
#ifndef FIXMATH_NO_OVERFLOW
|
||||
if (!bit)
|
||||
return (fix16_t)FIX16_OVERFLOW;
|
||||
#endif
|
||||
|
||||
if (divider & 0x80000000) {
|
||||
// Perform one step manually to avoid overflows later.
|
||||
// We know that divider's bottom bit is 0 here.
|
||||
if (remainder >= divider) {
|
||||
quotient |= bit;
|
||||
remainder -= divider;
|
||||
}
|
||||
divider >>= 1;
|
||||
bit >>= 1;
|
||||
}
|
||||
|
||||
/* Main division loop */
|
||||
while (bit && remainder) {
|
||||
if (remainder >= divider) {
|
||||
quotient |= bit;
|
||||
remainder -= divider;
|
||||
}
|
||||
|
||||
remainder <<= 1;
|
||||
bit >>= 1;
|
||||
}
|
||||
|
||||
#ifndef FIXMATH_NO_ROUNDING
|
||||
if (remainder >= divider) {
|
||||
quotient++;
|
||||
}
|
||||
#endif
|
||||
|
||||
fix16_t result = (fix16_t)quotient;
|
||||
|
||||
/* Figure out the sign of result */
|
||||
if ((a < 0) != (b < 0)) {
|
||||
#ifndef FIXMATH_NO_OVERFLOW
|
||||
if (result == FIX16_MINIMUM)
|
||||
return (fix16_t)FIX16_OVERFLOW;
|
||||
#endif
|
||||
|
||||
result = -result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static fix16_t fix16_sqrt(fix16_t x) {
|
||||
// It is assumed that x is not negative
|
||||
|
||||
uint32_t num = (uint32_t)x;
|
||||
uint32_t result = 0;
|
||||
uint32_t bit;
|
||||
uint8_t n;
|
||||
|
||||
bit = (uint32_t)1 << 30;
|
||||
while (bit > num)
|
||||
bit >>= 2;
|
||||
|
||||
// The main part is executed twice, in order to avoid
|
||||
// using 64 bit values in computations.
|
||||
for (n = 0; n < 2; n++) {
|
||||
// First we get the top 24 bits of the answer.
|
||||
while (bit) {
|
||||
if (num >= result + bit) {
|
||||
num -= result + bit;
|
||||
result = (result >> 1) + bit;
|
||||
} else {
|
||||
result = (result >> 1);
|
||||
}
|
||||
bit >>= 2;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
// Then process it again to get the lowest 8 bits.
|
||||
if (num > 65535) {
|
||||
// The remainder 'num' is too large to be shifted left
|
||||
// by 16, so we have to add 1 to result manually and
|
||||
// adjust 'num' accordingly.
|
||||
// num = a - (result + 0.5)^2
|
||||
// = num + result^2 - (result + 0.5)^2
|
||||
// = num - result - 0.5
|
||||
num -= result;
|
||||
num = (num << 16) - 0x8000;
|
||||
result = (result << 16) + 0x8000;
|
||||
} else {
|
||||
num <<= 16;
|
||||
result <<= 16;
|
||||
}
|
||||
|
||||
bit = 1 << 14;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef FIXMATH_NO_ROUNDING
|
||||
// Finally, if next bit would have been 1, round the result upwards.
|
||||
if (num > result) {
|
||||
result++;
|
||||
}
|
||||
#endif
|
||||
|
||||
return (fix16_t)result;
|
||||
}
|
||||
|
||||
static fix16_t fix16_exp(fix16_t x) {
|
||||
// Function to approximate exp(); optimized more for code size than speed
|
||||
|
||||
// exp(x) for x = +/- {1, 1/8, 1/64, 1/512}
|
||||
#define NUM_EXP_VALUES 4
|
||||
static const fix16_t exp_pos_values[NUM_EXP_VALUES] = {
|
||||
F16(2.7182818), F16(1.1331485), F16(1.0157477), F16(1.0019550)};
|
||||
static const fix16_t exp_neg_values[NUM_EXP_VALUES] = {
|
||||
F16(0.3678794), F16(0.8824969), F16(0.9844964), F16(0.9980488)};
|
||||
const fix16_t* exp_values;
|
||||
|
||||
fix16_t res, arg;
|
||||
uint16_t i;
|
||||
|
||||
if (x >= F16(10.3972))
|
||||
return FIX16_MAXIMUM;
|
||||
if (x <= F16(-11.7835))
|
||||
return 0;
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
exp_values = exp_neg_values;
|
||||
} else {
|
||||
exp_values = exp_pos_values;
|
||||
}
|
||||
|
||||
res = FIX16_ONE;
|
||||
arg = FIX16_ONE;
|
||||
for (i = 0; i < NUM_EXP_VALUES; i++) {
|
||||
while (x >= arg) {
|
||||
res = fix16_mul(res, exp_values[i]);
|
||||
x -= arg;
|
||||
}
|
||||
arg >>= 3;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void VocAlgorithm__init_instances(VocAlgorithmParams* params);
|
||||
static void
|
||||
VocAlgorithm__mean_variance_estimator__init(VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mean_variance_estimator___init_instances(
|
||||
VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mean_variance_estimator__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t std_initial,
|
||||
fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes);
|
||||
static void
|
||||
VocAlgorithm__mean_variance_estimator__set_states(VocAlgorithmParams* params,
|
||||
fix16_t mean, fix16_t std,
|
||||
fix16_t uptime_gamma);
|
||||
static fix16_t
|
||||
VocAlgorithm__mean_variance_estimator__get_std(VocAlgorithmParams* params);
|
||||
static fix16_t
|
||||
VocAlgorithm__mean_variance_estimator__get_mean(VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
|
||||
VocAlgorithmParams* params, fix16_t voc_index_from_prior);
|
||||
static void VocAlgorithm__mean_variance_estimator__process(
|
||||
VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior);
|
||||
static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
|
||||
VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K);
|
||||
static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
VocAlgorithmParams* params, fix16_t sample);
|
||||
static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
|
||||
fix16_t SRAW_STD,
|
||||
fix16_t SRAW_MEAN);
|
||||
static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
|
||||
fix16_t sraw);
|
||||
static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params);
|
||||
static void
|
||||
VocAlgorithm__sigmoid_scaled__set_parameters(VocAlgorithmParams* params,
|
||||
fix16_t offset);
|
||||
static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
|
||||
fix16_t sample);
|
||||
static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params);
|
||||
static void
|
||||
VocAlgorithm__adaptive_lowpass__set_parameters(VocAlgorithmParams* params);
|
||||
static fix16_t
|
||||
VocAlgorithm__adaptive_lowpass__process(VocAlgorithmParams* params,
|
||||
fix16_t sample);
|
||||
|
||||
void VocAlgorithm_init(VocAlgorithmParams* params) {
|
||||
|
||||
params->mVoc_Index_Offset = F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT);
|
||||
params->mTau_Mean_Variance_Hours =
|
||||
F16(VocAlgorithm_TAU_MEAN_VARIANCE_HOURS);
|
||||
params->mGating_Max_Duration_Minutes =
|
||||
F16(VocAlgorithm_GATING_MAX_DURATION_MINUTES);
|
||||
params->mSraw_Std_Initial = F16(VocAlgorithm_SRAW_STD_INITIAL);
|
||||
params->mUptime = F16(0.);
|
||||
params->mSraw = F16(0.);
|
||||
params->mVoc_Index = 0;
|
||||
VocAlgorithm__init_instances(params);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__init_instances(VocAlgorithmParams* params) {
|
||||
|
||||
VocAlgorithm__mean_variance_estimator__init(params);
|
||||
VocAlgorithm__mean_variance_estimator__set_parameters(
|
||||
params, params->mSraw_Std_Initial, params->mTau_Mean_Variance_Hours,
|
||||
params->mGating_Max_Duration_Minutes);
|
||||
VocAlgorithm__mox_model__init(params);
|
||||
VocAlgorithm__mox_model__set_parameters(
|
||||
params, VocAlgorithm__mean_variance_estimator__get_std(params),
|
||||
VocAlgorithm__mean_variance_estimator__get_mean(params));
|
||||
VocAlgorithm__sigmoid_scaled__init(params);
|
||||
VocAlgorithm__sigmoid_scaled__set_parameters(params,
|
||||
params->mVoc_Index_Offset);
|
||||
VocAlgorithm__adaptive_lowpass__init(params);
|
||||
VocAlgorithm__adaptive_lowpass__set_parameters(params);
|
||||
}
|
||||
|
||||
void VocAlgorithm_get_states(VocAlgorithmParams* params, int32_t* state0,
|
||||
int32_t* state1) {
|
||||
|
||||
*state0 = VocAlgorithm__mean_variance_estimator__get_mean(params);
|
||||
*state1 = VocAlgorithm__mean_variance_estimator__get_std(params);
|
||||
return;
|
||||
}
|
||||
|
||||
void VocAlgorithm_set_states(VocAlgorithmParams* params, int32_t state0,
|
||||
int32_t state1) {
|
||||
|
||||
VocAlgorithm__mean_variance_estimator__set_states(
|
||||
params, state0, state1, F16(VocAlgorithm_PERSISTENCE_UPTIME_GAMMA));
|
||||
params->mSraw = state0;
|
||||
}
|
||||
|
||||
void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams* params,
|
||||
int32_t voc_index_offset,
|
||||
int32_t learning_time_hours,
|
||||
int32_t gating_max_duration_minutes,
|
||||
int32_t std_initial) {
|
||||
|
||||
params->mVoc_Index_Offset = (fix16_from_int(voc_index_offset));
|
||||
params->mTau_Mean_Variance_Hours = (fix16_from_int(learning_time_hours));
|
||||
params->mGating_Max_Duration_Minutes =
|
||||
(fix16_from_int(gating_max_duration_minutes));
|
||||
params->mSraw_Std_Initial = (fix16_from_int(std_initial));
|
||||
VocAlgorithm__init_instances(params);
|
||||
}
|
||||
|
||||
void VocAlgorithm_process(VocAlgorithmParams* params, int32_t sraw,
|
||||
int32_t* voc_index) {
|
||||
|
||||
if ((params->mUptime <= F16(VocAlgorithm_INITIAL_BLACKOUT))) {
|
||||
params->mUptime =
|
||||
(params->mUptime + F16(VocAlgorithm_SAMPLING_INTERVAL));
|
||||
} else {
|
||||
if (((sraw > 0) && (sraw < 65000))) {
|
||||
if ((sraw < 20001)) {
|
||||
sraw = 20001;
|
||||
} else if ((sraw > 52767)) {
|
||||
sraw = 52767;
|
||||
}
|
||||
params->mSraw = (fix16_from_int((sraw - 20000)));
|
||||
}
|
||||
params->mVoc_Index =
|
||||
VocAlgorithm__mox_model__process(params, params->mSraw);
|
||||
params->mVoc_Index =
|
||||
VocAlgorithm__sigmoid_scaled__process(params, params->mVoc_Index);
|
||||
params->mVoc_Index =
|
||||
VocAlgorithm__adaptive_lowpass__process(params, params->mVoc_Index);
|
||||
if ((params->mVoc_Index < F16(0.5))) {
|
||||
params->mVoc_Index = F16(0.5);
|
||||
}
|
||||
if ((params->mSraw > F16(0.))) {
|
||||
VocAlgorithm__mean_variance_estimator__process(
|
||||
params, params->mSraw, params->mVoc_Index);
|
||||
VocAlgorithm__mox_model__set_parameters(
|
||||
params, VocAlgorithm__mean_variance_estimator__get_std(params),
|
||||
VocAlgorithm__mean_variance_estimator__get_mean(params));
|
||||
}
|
||||
}
|
||||
*voc_index = (fix16_cast_to_int((params->mVoc_Index + F16(0.5))));
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
VocAlgorithm__mean_variance_estimator__init(VocAlgorithmParams* params) {
|
||||
|
||||
VocAlgorithm__mean_variance_estimator__set_parameters(params, F16(0.),
|
||||
F16(0.), F16(0.));
|
||||
VocAlgorithm__mean_variance_estimator___init_instances(params);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator___init_instances(
|
||||
VocAlgorithmParams* params) {
|
||||
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__init(params);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t std_initial,
|
||||
fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes) {
|
||||
|
||||
params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes =
|
||||
gating_max_duration_minutes;
|
||||
params->m_Mean_Variance_Estimator___Initialized = false;
|
||||
params->m_Mean_Variance_Estimator___Mean = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Sraw_Offset = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Std = std_initial;
|
||||
params->m_Mean_Variance_Estimator___Gamma =
|
||||
(fix16_div(F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
|
||||
(VocAlgorithm_SAMPLING_INTERVAL / 3600.))),
|
||||
(tau_mean_variance_hours +
|
||||
F16((VocAlgorithm_SAMPLING_INTERVAL / 3600.)))));
|
||||
params->m_Mean_Variance_Estimator___Gamma_Initial_Mean =
|
||||
F16(((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
|
||||
VocAlgorithm_SAMPLING_INTERVAL) /
|
||||
(VocAlgorithm_TAU_INITIAL_MEAN + VocAlgorithm_SAMPLING_INTERVAL)));
|
||||
params->m_Mean_Variance_Estimator___Gamma_Initial_Variance = F16(
|
||||
((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
|
||||
VocAlgorithm_SAMPLING_INTERVAL) /
|
||||
(VocAlgorithm_TAU_INITIAL_VARIANCE + VocAlgorithm_SAMPLING_INTERVAL)));
|
||||
params->m_Mean_Variance_Estimator__Gamma_Mean = F16(0.);
|
||||
params->m_Mean_Variance_Estimator__Gamma_Variance = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gamma = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
|
||||
}
|
||||
|
||||
static void
|
||||
VocAlgorithm__mean_variance_estimator__set_states(VocAlgorithmParams* params,
|
||||
fix16_t mean, fix16_t std,
|
||||
fix16_t uptime_gamma) {
|
||||
|
||||
params->m_Mean_Variance_Estimator___Mean = mean;
|
||||
params->m_Mean_Variance_Estimator___Std = std;
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gamma = uptime_gamma;
|
||||
params->m_Mean_Variance_Estimator___Initialized = true;
|
||||
}
|
||||
|
||||
static fix16_t
|
||||
VocAlgorithm__mean_variance_estimator__get_std(VocAlgorithmParams* params) {
|
||||
|
||||
return params->m_Mean_Variance_Estimator___Std;
|
||||
}
|
||||
|
||||
static fix16_t
|
||||
VocAlgorithm__mean_variance_estimator__get_mean(VocAlgorithmParams* params) {
|
||||
|
||||
return (params->m_Mean_Variance_Estimator___Mean +
|
||||
params->m_Mean_Variance_Estimator___Sraw_Offset);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
|
||||
VocAlgorithmParams* params, fix16_t voc_index_from_prior) {
|
||||
|
||||
fix16_t uptime_limit;
|
||||
fix16_t sigmoid_gamma_mean;
|
||||
fix16_t gamma_mean;
|
||||
fix16_t gating_threshold_mean;
|
||||
fix16_t sigmoid_gating_mean;
|
||||
fix16_t sigmoid_gamma_variance;
|
||||
fix16_t gamma_variance;
|
||||
fix16_t gating_threshold_variance;
|
||||
fix16_t sigmoid_gating_variance;
|
||||
|
||||
uptime_limit = F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX -
|
||||
VocAlgorithm_SAMPLING_INTERVAL));
|
||||
if ((params->m_Mean_Variance_Estimator___Uptime_Gamma < uptime_limit)) {
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gamma =
|
||||
(params->m_Mean_Variance_Estimator___Uptime_Gamma +
|
||||
F16(VocAlgorithm_SAMPLING_INTERVAL));
|
||||
}
|
||||
if ((params->m_Mean_Variance_Estimator___Uptime_Gating < uptime_limit)) {
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gating =
|
||||
(params->m_Mean_Variance_Estimator___Uptime_Gating +
|
||||
F16(VocAlgorithm_SAMPLING_INTERVAL));
|
||||
}
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(1.), F16(VocAlgorithm_INIT_DURATION_MEAN),
|
||||
F16(VocAlgorithm_INIT_TRANSITION_MEAN));
|
||||
sigmoid_gamma_mean =
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
|
||||
gamma_mean =
|
||||
(params->m_Mean_Variance_Estimator___Gamma +
|
||||
(fix16_mul((params->m_Mean_Variance_Estimator___Gamma_Initial_Mean -
|
||||
params->m_Mean_Variance_Estimator___Gamma),
|
||||
sigmoid_gamma_mean)));
|
||||
gating_threshold_mean =
|
||||
(F16(VocAlgorithm_GATING_THRESHOLD) +
|
||||
(fix16_mul(
|
||||
F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
|
||||
VocAlgorithm_GATING_THRESHOLD)),
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(1.), gating_threshold_mean,
|
||||
F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
|
||||
sigmoid_gating_mean =
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, voc_index_from_prior);
|
||||
params->m_Mean_Variance_Estimator__Gamma_Mean =
|
||||
(fix16_mul(sigmoid_gating_mean, gamma_mean));
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(1.), F16(VocAlgorithm_INIT_DURATION_VARIANCE),
|
||||
F16(VocAlgorithm_INIT_TRANSITION_VARIANCE));
|
||||
sigmoid_gamma_variance =
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
|
||||
gamma_variance =
|
||||
(params->m_Mean_Variance_Estimator___Gamma +
|
||||
(fix16_mul(
|
||||
(params->m_Mean_Variance_Estimator___Gamma_Initial_Variance -
|
||||
params->m_Mean_Variance_Estimator___Gamma),
|
||||
(sigmoid_gamma_variance - sigmoid_gamma_mean))));
|
||||
gating_threshold_variance =
|
||||
(F16(VocAlgorithm_GATING_THRESHOLD) +
|
||||
(fix16_mul(
|
||||
F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
|
||||
VocAlgorithm_GATING_THRESHOLD)),
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(1.), gating_threshold_variance,
|
||||
F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
|
||||
sigmoid_gating_variance =
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, voc_index_from_prior);
|
||||
params->m_Mean_Variance_Estimator__Gamma_Variance =
|
||||
(fix16_mul(sigmoid_gating_variance, gamma_variance));
|
||||
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes =
|
||||
(params->m_Mean_Variance_Estimator___Gating_Duration_Minutes +
|
||||
(fix16_mul(F16((VocAlgorithm_SAMPLING_INTERVAL / 60.)),
|
||||
((fix16_mul((F16(1.) - sigmoid_gating_mean),
|
||||
F16((1. + VocAlgorithm_GATING_MAX_RATIO)))) -
|
||||
F16(VocAlgorithm_GATING_MAX_RATIO)))));
|
||||
if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes <
|
||||
F16(0.))) {
|
||||
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
|
||||
}
|
||||
if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes >
|
||||
params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes)) {
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
|
||||
}
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator__process(
|
||||
VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior) {
|
||||
|
||||
fix16_t delta_sgp;
|
||||
fix16_t c;
|
||||
fix16_t additional_scaling;
|
||||
|
||||
if ((params->m_Mean_Variance_Estimator___Initialized == false)) {
|
||||
params->m_Mean_Variance_Estimator___Initialized = true;
|
||||
params->m_Mean_Variance_Estimator___Sraw_Offset = sraw;
|
||||
params->m_Mean_Variance_Estimator___Mean = F16(0.);
|
||||
} else {
|
||||
if (((params->m_Mean_Variance_Estimator___Mean >= F16(100.)) ||
|
||||
(params->m_Mean_Variance_Estimator___Mean <= F16(-100.)))) {
|
||||
params->m_Mean_Variance_Estimator___Sraw_Offset =
|
||||
(params->m_Mean_Variance_Estimator___Sraw_Offset +
|
||||
params->m_Mean_Variance_Estimator___Mean);
|
||||
params->m_Mean_Variance_Estimator___Mean = F16(0.);
|
||||
}
|
||||
sraw = (sraw - params->m_Mean_Variance_Estimator___Sraw_Offset);
|
||||
VocAlgorithm__mean_variance_estimator___calculate_gamma(
|
||||
params, voc_index_from_prior);
|
||||
delta_sgp = (fix16_div(
|
||||
(sraw - params->m_Mean_Variance_Estimator___Mean),
|
||||
F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING)));
|
||||
if ((delta_sgp < F16(0.))) {
|
||||
c = (params->m_Mean_Variance_Estimator___Std - delta_sgp);
|
||||
} else {
|
||||
c = (params->m_Mean_Variance_Estimator___Std + delta_sgp);
|
||||
}
|
||||
additional_scaling = F16(1.);
|
||||
if ((c > F16(1440.))) {
|
||||
additional_scaling = F16(4.);
|
||||
}
|
||||
params->m_Mean_Variance_Estimator___Std = (fix16_mul(
|
||||
fix16_sqrt((fix16_mul(
|
||||
additional_scaling,
|
||||
(F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) -
|
||||
params->m_Mean_Variance_Estimator__Gamma_Variance)))),
|
||||
fix16_sqrt((
|
||||
(fix16_mul(
|
||||
params->m_Mean_Variance_Estimator___Std,
|
||||
(fix16_div(
|
||||
params->m_Mean_Variance_Estimator___Std,
|
||||
(fix16_mul(
|
||||
F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING),
|
||||
additional_scaling)))))) +
|
||||
(fix16_mul(
|
||||
(fix16_div(
|
||||
(fix16_mul(
|
||||
params->m_Mean_Variance_Estimator__Gamma_Variance,
|
||||
delta_sgp)),
|
||||
additional_scaling)),
|
||||
delta_sgp))))));
|
||||
params->m_Mean_Variance_Estimator___Mean =
|
||||
(params->m_Mean_Variance_Estimator___Mean +
|
||||
(fix16_mul(params->m_Mean_Variance_Estimator__Gamma_Mean,
|
||||
delta_sgp)));
|
||||
}
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
|
||||
VocAlgorithmParams* params) {
|
||||
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(0.), F16(0.), F16(0.));
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K) {
|
||||
|
||||
params->m_Mean_Variance_Estimator___Sigmoid__L = L;
|
||||
params->m_Mean_Variance_Estimator___Sigmoid__K = K;
|
||||
params->m_Mean_Variance_Estimator___Sigmoid__X0 = X0;
|
||||
}
|
||||
|
||||
static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
VocAlgorithmParams* params, fix16_t sample) {
|
||||
|
||||
fix16_t x;
|
||||
|
||||
x = (fix16_mul(params->m_Mean_Variance_Estimator___Sigmoid__K,
|
||||
(sample - params->m_Mean_Variance_Estimator___Sigmoid__X0)));
|
||||
if ((x < F16(-50.))) {
|
||||
return params->m_Mean_Variance_Estimator___Sigmoid__L;
|
||||
} else if ((x > F16(50.))) {
|
||||
return F16(0.);
|
||||
} else {
|
||||
return (fix16_div(params->m_Mean_Variance_Estimator___Sigmoid__L,
|
||||
(F16(1.) + fix16_exp(x))));
|
||||
}
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params) {
|
||||
|
||||
VocAlgorithm__mox_model__set_parameters(params, F16(1.), F16(0.));
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
|
||||
fix16_t SRAW_STD,
|
||||
fix16_t SRAW_MEAN) {
|
||||
|
||||
params->m_Mox_Model__Sraw_Std = SRAW_STD;
|
||||
params->m_Mox_Model__Sraw_Mean = SRAW_MEAN;
|
||||
}
|
||||
|
||||
static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
|
||||
fix16_t sraw) {
|
||||
|
||||
return (fix16_mul((fix16_div((sraw - params->m_Mox_Model__Sraw_Mean),
|
||||
(-(params->m_Mox_Model__Sraw_Std +
|
||||
F16(VocAlgorithm_SRAW_STD_BONUS))))),
|
||||
F16(VocAlgorithm_VOC_INDEX_GAIN)));
|
||||
}
|
||||
|
||||
static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params) {
|
||||
|
||||
VocAlgorithm__sigmoid_scaled__set_parameters(params, F16(0.));
|
||||
}
|
||||
|
||||
static void
|
||||
VocAlgorithm__sigmoid_scaled__set_parameters(VocAlgorithmParams* params,
|
||||
fix16_t offset) {
|
||||
|
||||
params->m_Sigmoid_Scaled__Offset = offset;
|
||||
}
|
||||
|
||||
static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
|
||||
fix16_t sample) {
|
||||
|
||||
fix16_t x;
|
||||
fix16_t shift;
|
||||
|
||||
x = (fix16_mul(F16(VocAlgorithm_SIGMOID_K),
|
||||
(sample - F16(VocAlgorithm_SIGMOID_X0))));
|
||||
if ((x < F16(-50.))) {
|
||||
return F16(VocAlgorithm_SIGMOID_L);
|
||||
} else if ((x > F16(50.))) {
|
||||
return F16(0.);
|
||||
} else {
|
||||
if ((sample >= F16(0.))) {
|
||||
shift = (fix16_div(
|
||||
(F16(VocAlgorithm_SIGMOID_L) -
|
||||
(fix16_mul(F16(5.), params->m_Sigmoid_Scaled__Offset))),
|
||||
F16(4.)));
|
||||
return ((fix16_div((F16(VocAlgorithm_SIGMOID_L) + shift),
|
||||
(F16(1.) + fix16_exp(x)))) -
|
||||
shift);
|
||||
} else {
|
||||
return (fix16_mul(
|
||||
(fix16_div(params->m_Sigmoid_Scaled__Offset,
|
||||
F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT))),
|
||||
(fix16_div(F16(VocAlgorithm_SIGMOID_L),
|
||||
(F16(1.) + fix16_exp(x))))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params) {
|
||||
|
||||
VocAlgorithm__adaptive_lowpass__set_parameters(params);
|
||||
}
|
||||
|
||||
static void
|
||||
VocAlgorithm__adaptive_lowpass__set_parameters(VocAlgorithmParams* params) {
|
||||
|
||||
params->m_Adaptive_Lowpass__A1 =
|
||||
F16((VocAlgorithm_SAMPLING_INTERVAL /
|
||||
(VocAlgorithm_LP_TAU_FAST + VocAlgorithm_SAMPLING_INTERVAL)));
|
||||
params->m_Adaptive_Lowpass__A2 =
|
||||
F16((VocAlgorithm_SAMPLING_INTERVAL /
|
||||
(VocAlgorithm_LP_TAU_SLOW + VocAlgorithm_SAMPLING_INTERVAL)));
|
||||
params->m_Adaptive_Lowpass___Initialized = false;
|
||||
}
|
||||
|
||||
static fix16_t
|
||||
VocAlgorithm__adaptive_lowpass__process(VocAlgorithmParams* params,
|
||||
fix16_t sample) {
|
||||
|
||||
fix16_t abs_delta;
|
||||
fix16_t F1;
|
||||
fix16_t tau_a;
|
||||
fix16_t a3;
|
||||
|
||||
if ((params->m_Adaptive_Lowpass___Initialized == false)) {
|
||||
params->m_Adaptive_Lowpass___X1 = sample;
|
||||
params->m_Adaptive_Lowpass___X2 = sample;
|
||||
params->m_Adaptive_Lowpass___X3 = sample;
|
||||
params->m_Adaptive_Lowpass___Initialized = true;
|
||||
}
|
||||
params->m_Adaptive_Lowpass___X1 =
|
||||
((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A1),
|
||||
params->m_Adaptive_Lowpass___X1)) +
|
||||
(fix16_mul(params->m_Adaptive_Lowpass__A1, sample)));
|
||||
params->m_Adaptive_Lowpass___X2 =
|
||||
((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A2),
|
||||
params->m_Adaptive_Lowpass___X2)) +
|
||||
(fix16_mul(params->m_Adaptive_Lowpass__A2, sample)));
|
||||
abs_delta =
|
||||
(params->m_Adaptive_Lowpass___X1 - params->m_Adaptive_Lowpass___X2);
|
||||
if ((abs_delta < F16(0.))) {
|
||||
abs_delta = (-abs_delta);
|
||||
}
|
||||
F1 = fix16_exp((fix16_mul(F16(VocAlgorithm_LP_ALPHA), abs_delta)));
|
||||
tau_a =
|
||||
((fix16_mul(F16((VocAlgorithm_LP_TAU_SLOW - VocAlgorithm_LP_TAU_FAST)),
|
||||
F1)) +
|
||||
F16(VocAlgorithm_LP_TAU_FAST));
|
||||
a3 = (fix16_div(F16(VocAlgorithm_SAMPLING_INTERVAL),
|
||||
(F16(VocAlgorithm_SAMPLING_INTERVAL) + tau_a)));
|
||||
params->m_Adaptive_Lowpass___X3 =
|
||||
((fix16_mul((F16(1.) - a3), params->m_Adaptive_Lowpass___X3)) +
|
||||
(fix16_mul(a3, sample)));
|
||||
return params->m_Adaptive_Lowpass___X3;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
IndentWidth: 2
|
||||
ColumnLimit: 80
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BreakBeforeBraces: Attach
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
SpacesBeforeTrailingComments: 1
|
||||
@@ -0,0 +1,127 @@
|
||||
# Adafruit Community Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and leaders pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level or type of
|
||||
experience, education, socio-economic status, nationality, personal appearance,
|
||||
race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
We are committed to providing a friendly, safe and welcoming environment for
|
||||
all.
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Be kind and courteous to others
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Collaborating with other community members
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and sexual attention or advances
|
||||
* The use of inappropriate images, including in a community member's avatar
|
||||
* The use of inappropriate language, including in a community member's nickname
|
||||
* Any spamming, flaming, baiting or other attention-stealing behavior
|
||||
* Excessive or unwelcome helping; answering outside the scope of the question
|
||||
asked
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate
|
||||
|
||||
The goal of the standards and moderation guidelines outlined here is to build
|
||||
and maintain a respectful community. We ask that you don’t just aim to be
|
||||
"technically unimpeachable", but rather try to be your best self.
|
||||
|
||||
We value many things beyond technical expertise, including collaboration and
|
||||
supporting others within our community. Providing a positive experience for
|
||||
other community members can have a much more significant impact than simply
|
||||
providing the correct answer.
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project leaders are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project leaders have the right and responsibility to remove, edit, or
|
||||
reject messages, comments, commits, code, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any community member for other behaviors that they deem
|
||||
inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Moderation
|
||||
|
||||
Instances of behaviors that violate the Adafruit Community Code of Conduct
|
||||
may be reported by any member of the community. Community members are
|
||||
encouraged to report these situations, including situations they witness
|
||||
involving other community members.
|
||||
|
||||
You may report in the following ways:
|
||||
|
||||
In any situation, you may send an email to <support@adafruit.com>.
|
||||
|
||||
On the Adafruit Discord, you may send an open message from any channel
|
||||
to all Community Helpers by tagging @community helpers. You may also send an
|
||||
open message from any channel, or a direct message to @kattni#1507,
|
||||
@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or
|
||||
@Andon#8175.
|
||||
|
||||
Email and direct message reports will be kept confidential.
|
||||
|
||||
In situations on Discord where the issue is particularly egregious, possibly
|
||||
illegal, requires immediate action, or violates the Discord terms of service,
|
||||
you should also report the message directly to Discord.
|
||||
|
||||
These are the steps for upholding our community’s standards of conduct.
|
||||
|
||||
1. Any member of the community may report any situation that violates the
|
||||
Adafruit Community Code of Conduct. All reports will be reviewed and
|
||||
investigated.
|
||||
2. If the behavior is an egregious violation, the community member who
|
||||
committed the violation may be banned immediately, without warning.
|
||||
3. Otherwise, moderators will first respond to such behavior with a warning.
|
||||
4. Moderators follow a soft "three strikes" policy - the community member may
|
||||
be given another chance, if they are receptive to the warning and change their
|
||||
behavior.
|
||||
5. If the community member is unreceptive or unreasonable when warned by a
|
||||
moderator, or the warning goes unheeded, they may be banned for a first or
|
||||
second offense. Repeated offenses will result in the community member being
|
||||
banned.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct and the enforcement policies listed above apply to all
|
||||
Adafruit Community venues. This includes but is not limited to any community
|
||||
spaces (both public and private), the entire Adafruit Discord server, and
|
||||
Adafruit GitHub repositories. Examples of Adafruit Community spaces include
|
||||
but are not limited to meet-ups, audio chats on the Adafruit Discord, or
|
||||
interaction at a conference.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. As a community
|
||||
member, you are representing our community, and are expected to behave
|
||||
accordingly.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
<https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>,
|
||||
and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html).
|
||||
|
||||
For other projects adopting the Adafruit Community Code of
|
||||
Conduct, please contact the maintainers of those projects for enforcement.
|
||||
If you wish to use this code of conduct for your own project, consider
|
||||
explicitly mentioning your moderation policy or making a copy with your
|
||||
own moderation policy so as to avoid confusion.
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name=Adafruit SGP40 Sensor
|
||||
version=1.1.0
|
||||
version=1.1.4
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=This is an Arduino library for the Adafruit SGP40 Gas / Air Quality Sensor
|
||||
+6
-6
@@ -26,9 +26,10 @@
|
||||
*/
|
||||
|
||||
#include "Adafruit_SGP40.h"
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
//#define I2C_DEBUG
|
||||
// #define I2C_DEBUG
|
||||
|
||||
/*!
|
||||
* @brief Instantiates a new SGP40 class
|
||||
@@ -43,7 +44,7 @@ Adafruit_SGP40::Adafruit_SGP40() {}
|
||||
* Optional pointer to I2C interface, otherwise use Wire
|
||||
* @return True if SGP40 found on I2C, False if something went wrong!
|
||||
*/
|
||||
boolean Adafruit_SGP40::begin(TwoWire *theWire) {
|
||||
boolean Adafruit_SGP40::begin(TwoWire* theWire) {
|
||||
if (i2c_dev) {
|
||||
delete i2c_dev; // remove old interface
|
||||
}
|
||||
@@ -112,7 +113,7 @@ bool Adafruit_SGP40::selfTest(void) {
|
||||
command[0] = 0x28;
|
||||
command[1] = 0x0E;
|
||||
|
||||
if (!readWordFromCommand(command, 2, 250, &reply, 1))
|
||||
if (!readWordFromCommand(command, 2, 500, &reply, 1))
|
||||
return false;
|
||||
if ((reply == 0xD400)) {
|
||||
return true;
|
||||
@@ -172,9 +173,8 @@ uint16_t Adafruit_SGP40::measureRaw(float temperature, float humidity) {
|
||||
|
||||
bool Adafruit_SGP40::readWordFromCommand(uint8_t command[],
|
||||
uint8_t commandLength,
|
||||
uint16_t delayms, uint16_t *readdata,
|
||||
uint16_t delayms, uint16_t* readdata,
|
||||
uint8_t readlen) {
|
||||
|
||||
if (!i2c_dev->write(command, commandLength)) {
|
||||
return false;
|
||||
}
|
||||
@@ -213,7 +213,7 @@ bool Adafruit_SGP40::readWordFromCommand(uint8_t command[],
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t Adafruit_SGP40::generateCRC(uint8_t *data, uint8_t datalen) {
|
||||
uint8_t Adafruit_SGP40::generateCRC(uint8_t* data, uint8_t datalen) {
|
||||
// calculates 8-Bit checksum with given polynomial
|
||||
uint8_t crc = SGP40_CRC8_INIT;
|
||||
|
||||
+9
-8
@@ -21,9 +21,10 @@
|
||||
#ifndef ADAFRUIT_SGP40_H
|
||||
#define ADAFRUIT_SGP40_H
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <Adafruit_BusIO_Register.h>
|
||||
#include <Adafruit_I2CDevice.h>
|
||||
|
||||
#include "Arduino.h"
|
||||
extern "C" {
|
||||
#include "sensirion_arch_config.h"
|
||||
#include "sensirion_voc_algorithm.h"
|
||||
@@ -43,9 +44,9 @@ extern "C" {
|
||||
* SGP40 Gas Sensor
|
||||
*/
|
||||
class Adafruit_SGP40 {
|
||||
public:
|
||||
public:
|
||||
Adafruit_SGP40();
|
||||
bool begin(TwoWire *theWire = &Wire);
|
||||
bool begin(TwoWire* theWire = &Wire);
|
||||
bool selfTest(void);
|
||||
|
||||
bool softReset();
|
||||
@@ -57,12 +58,12 @@ public:
|
||||
* **/
|
||||
uint16_t serialnumber[3];
|
||||
|
||||
private:
|
||||
Adafruit_I2CDevice *i2c_dev = NULL; ///< Pointer to I2C bus interface
|
||||
void write(uint8_t address, uint8_t *data, uint8_t n);
|
||||
void read(uint8_t address, uint8_t *data, uint8_t n);
|
||||
private:
|
||||
Adafruit_I2CDevice* i2c_dev = NULL; ///< Pointer to I2C bus interface
|
||||
void write(uint8_t address, uint8_t* data, uint8_t n);
|
||||
void read(uint8_t address, uint8_t* data, uint8_t n);
|
||||
bool readWordFromCommand(uint8_t command[], uint8_t commandLength,
|
||||
uint16_t delayms, uint16_t *readdata = NULL,
|
||||
uint16_t delayms, uint16_t* readdata = NULL,
|
||||
uint8_t readlen = 0);
|
||||
uint8_t generateCRC(uint8_t data[], uint8_t datalen);
|
||||
|
||||
@@ -0,0 +1,766 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Sensirion AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Sensirion AG nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "sensirion_voc_algorithm.h"
|
||||
|
||||
/* The fixed point arithmetic parts of this code were originally created by
|
||||
* https://github.com/PetteriAimonen/libfixmath
|
||||
*/
|
||||
|
||||
/*!< the maximum value of fix16_t */
|
||||
#define FIX16_MAXIMUM 0x7FFFFFFF
|
||||
/*!< the minimum value of fix16_t */
|
||||
#define FIX16_MINIMUM 0x80000000
|
||||
/*!< the value used to indicate overflows when FIXMATH_NO_OVERFLOW is not
|
||||
* specified */
|
||||
#define FIX16_OVERFLOW 0x80000000
|
||||
/*!< fix16_t value of 1 */
|
||||
#define FIX16_ONE 0x00010000
|
||||
|
||||
static inline fix16_t fix16_from_int(int32_t a) {
|
||||
return a * FIX16_ONE;
|
||||
}
|
||||
|
||||
static inline int32_t fix16_cast_to_int(fix16_t a) {
|
||||
return (a >= 0) ? (a >> 16) : -((-a) >> 16);
|
||||
}
|
||||
|
||||
/*! Multiplies the two given fix16_t's and returns the result. */
|
||||
static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1);
|
||||
|
||||
/*! Divides the first given fix16_t by the second and returns the result. */
|
||||
static fix16_t fix16_div(fix16_t inArg0, fix16_t inArg1);
|
||||
|
||||
/*! Returns the square root of the given fix16_t. */
|
||||
static fix16_t fix16_sqrt(fix16_t inValue);
|
||||
|
||||
/*! Returns the exponent (e^) of the given fix16_t. */
|
||||
static fix16_t fix16_exp(fix16_t inValue);
|
||||
|
||||
static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) {
|
||||
// Each argument is divided to 16-bit parts.
|
||||
// AB
|
||||
// * CD
|
||||
// -----------
|
||||
// BD 16 * 16 -> 32 bit products
|
||||
// CB
|
||||
// AD
|
||||
// AC
|
||||
// |----| 64 bit product
|
||||
uint32_t absArg0 = (uint32_t)((inArg0 >= 0) ? inArg0 : (-inArg0));
|
||||
uint32_t absArg1 = (uint32_t)((inArg1 >= 0) ? inArg1 : (-inArg1));
|
||||
uint32_t A = (absArg0 >> 16), C = (absArg1 >> 16);
|
||||
uint32_t B = (absArg0 & 0xFFFF), D = (absArg1 & 0xFFFF);
|
||||
|
||||
uint32_t AC = A * C;
|
||||
uint32_t AD_CB = A * D + C * B;
|
||||
uint32_t BD = B * D;
|
||||
|
||||
uint32_t product_hi = AC + (AD_CB >> 16);
|
||||
|
||||
// Handle carry from lower 32 bits to upper part of result.
|
||||
uint32_t ad_cb_temp = AD_CB << 16;
|
||||
uint32_t product_lo = BD + ad_cb_temp;
|
||||
if (product_lo < BD)
|
||||
product_hi++;
|
||||
|
||||
#ifndef FIXMATH_NO_OVERFLOW
|
||||
// The upper 17 bits should all be zero.
|
||||
if (product_hi >> 15)
|
||||
return (fix16_t)FIX16_OVERFLOW;
|
||||
#endif
|
||||
|
||||
#ifdef FIXMATH_NO_ROUNDING
|
||||
fix16_t result = (fix16_t)((product_hi << 16) | (product_lo >> 16));
|
||||
if ((inArg0 < 0) != (inArg1 < 0))
|
||||
result = -result;
|
||||
return result;
|
||||
#else
|
||||
// Adding 0x8000 (= 0.5) and then using right shift
|
||||
// achieves proper rounding to result.
|
||||
// Handle carry from lower to upper part.
|
||||
uint32_t product_lo_tmp = product_lo;
|
||||
product_lo += 0x8000;
|
||||
if (product_lo < product_lo_tmp)
|
||||
product_hi++;
|
||||
|
||||
// Discard the lowest 16 bits and convert back to signed result.
|
||||
fix16_t result = (fix16_t)((product_hi << 16) | (product_lo >> 16));
|
||||
if ((inArg0 < 0) != (inArg1 < 0))
|
||||
result = -result;
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
static fix16_t fix16_div(fix16_t a, fix16_t b) {
|
||||
// This uses the basic binary restoring division algorithm.
|
||||
// It appears to be faster to do the whole division manually than
|
||||
// trying to compose a 64-bit divide out of 32-bit divisions on
|
||||
// platforms without hardware divide.
|
||||
|
||||
if (b == 0)
|
||||
return (fix16_t)FIX16_MINIMUM;
|
||||
|
||||
uint32_t remainder = (uint32_t)((a >= 0) ? a : (-a));
|
||||
uint32_t divider = (uint32_t)((b >= 0) ? b : (-b));
|
||||
|
||||
uint32_t quotient = 0;
|
||||
uint32_t bit = 0x10000;
|
||||
|
||||
/* The algorithm requires D >= R */
|
||||
while (divider < remainder) {
|
||||
divider <<= 1;
|
||||
bit <<= 1;
|
||||
}
|
||||
|
||||
#ifndef FIXMATH_NO_OVERFLOW
|
||||
if (!bit)
|
||||
return (fix16_t)FIX16_OVERFLOW;
|
||||
#endif
|
||||
|
||||
if (divider & 0x80000000) {
|
||||
// Perform one step manually to avoid overflows later.
|
||||
// We know that divider's bottom bit is 0 here.
|
||||
if (remainder >= divider) {
|
||||
quotient |= bit;
|
||||
remainder -= divider;
|
||||
}
|
||||
divider >>= 1;
|
||||
bit >>= 1;
|
||||
}
|
||||
|
||||
/* Main division loop */
|
||||
while (bit && remainder) {
|
||||
if (remainder >= divider) {
|
||||
quotient |= bit;
|
||||
remainder -= divider;
|
||||
}
|
||||
|
||||
remainder <<= 1;
|
||||
bit >>= 1;
|
||||
}
|
||||
|
||||
#ifndef FIXMATH_NO_ROUNDING
|
||||
if (remainder >= divider) {
|
||||
quotient++;
|
||||
}
|
||||
#endif
|
||||
|
||||
fix16_t result = (fix16_t)quotient;
|
||||
|
||||
/* Figure out the sign of result */
|
||||
if ((a < 0) != (b < 0)) {
|
||||
#ifndef FIXMATH_NO_OVERFLOW
|
||||
if (result == FIX16_MINIMUM)
|
||||
return (fix16_t)FIX16_OVERFLOW;
|
||||
#endif
|
||||
|
||||
result = -result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static fix16_t fix16_sqrt(fix16_t x) {
|
||||
// It is assumed that x is not negative
|
||||
|
||||
uint32_t num = (uint32_t)x;
|
||||
uint32_t result = 0;
|
||||
uint32_t bit;
|
||||
uint8_t n;
|
||||
|
||||
bit = (uint32_t)1 << 30;
|
||||
while (bit > num)
|
||||
bit >>= 2;
|
||||
|
||||
// The main part is executed twice, in order to avoid
|
||||
// using 64 bit values in computations.
|
||||
for (n = 0; n < 2; n++) {
|
||||
// First we get the top 24 bits of the answer.
|
||||
while (bit) {
|
||||
if (num >= result + bit) {
|
||||
num -= result + bit;
|
||||
result = (result >> 1) + bit;
|
||||
} else {
|
||||
result = (result >> 1);
|
||||
}
|
||||
bit >>= 2;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
// Then process it again to get the lowest 8 bits.
|
||||
if (num > 65535) {
|
||||
// The remainder 'num' is too large to be shifted left
|
||||
// by 16, so we have to add 1 to result manually and
|
||||
// adjust 'num' accordingly.
|
||||
// num = a - (result + 0.5)^2
|
||||
// = num + result^2 - (result + 0.5)^2
|
||||
// = num - result - 0.5
|
||||
num -= result;
|
||||
num = (num << 16) - 0x8000;
|
||||
result = (result << 16) + 0x8000;
|
||||
} else {
|
||||
num <<= 16;
|
||||
result <<= 16;
|
||||
}
|
||||
|
||||
bit = 1 << 14;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef FIXMATH_NO_ROUNDING
|
||||
// Finally, if next bit would have been 1, round the result upwards.
|
||||
if (num > result) {
|
||||
result++;
|
||||
}
|
||||
#endif
|
||||
|
||||
return (fix16_t)result;
|
||||
}
|
||||
|
||||
static fix16_t fix16_exp(fix16_t x) {
|
||||
// Function to approximate exp(); optimized more for code size than speed
|
||||
|
||||
// exp(x) for x = +/- {1, 1/8, 1/64, 1/512}
|
||||
#define NUM_EXP_VALUES 4
|
||||
static const fix16_t exp_pos_values[NUM_EXP_VALUES] = {
|
||||
F16(2.7182818), F16(1.1331485), F16(1.0157477), F16(1.0019550)};
|
||||
static const fix16_t exp_neg_values[NUM_EXP_VALUES] = {
|
||||
F16(0.3678794), F16(0.8824969), F16(0.9844964), F16(0.9980488)};
|
||||
const fix16_t* exp_values;
|
||||
|
||||
fix16_t res, arg;
|
||||
uint16_t i;
|
||||
|
||||
if (x >= F16(10.3972))
|
||||
return FIX16_MAXIMUM;
|
||||
if (x <= F16(-11.7835))
|
||||
return 0;
|
||||
|
||||
if (x < 0) {
|
||||
x = -x;
|
||||
exp_values = exp_neg_values;
|
||||
} else {
|
||||
exp_values = exp_pos_values;
|
||||
}
|
||||
|
||||
res = FIX16_ONE;
|
||||
arg = FIX16_ONE;
|
||||
for (i = 0; i < NUM_EXP_VALUES; i++) {
|
||||
while (x >= arg) {
|
||||
res = fix16_mul(res, exp_values[i]);
|
||||
x -= arg;
|
||||
}
|
||||
arg >>= 3;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void VocAlgorithm__init_instances(VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mean_variance_estimator__init(
|
||||
VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mean_variance_estimator___init_instances(
|
||||
VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mean_variance_estimator__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t std_initial,
|
||||
fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes);
|
||||
static void VocAlgorithm__mean_variance_estimator__set_states(
|
||||
VocAlgorithmParams* params, fix16_t mean, fix16_t std,
|
||||
fix16_t uptime_gamma);
|
||||
static fix16_t VocAlgorithm__mean_variance_estimator__get_std(
|
||||
VocAlgorithmParams* params);
|
||||
static fix16_t VocAlgorithm__mean_variance_estimator__get_mean(
|
||||
VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
|
||||
VocAlgorithmParams* params, fix16_t voc_index_from_prior);
|
||||
static void VocAlgorithm__mean_variance_estimator__process(
|
||||
VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior);
|
||||
static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
|
||||
VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K);
|
||||
static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
VocAlgorithmParams* params, fix16_t sample);
|
||||
static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
|
||||
fix16_t SRAW_STD,
|
||||
fix16_t SRAW_MEAN);
|
||||
static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
|
||||
fix16_t sraw);
|
||||
static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__sigmoid_scaled__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t offset);
|
||||
static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
|
||||
fix16_t sample);
|
||||
static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params);
|
||||
static void VocAlgorithm__adaptive_lowpass__set_parameters(
|
||||
VocAlgorithmParams* params);
|
||||
static fix16_t VocAlgorithm__adaptive_lowpass__process(
|
||||
VocAlgorithmParams* params, fix16_t sample);
|
||||
|
||||
void VocAlgorithm_init(VocAlgorithmParams* params) {
|
||||
params->mVoc_Index_Offset = F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT);
|
||||
params->mTau_Mean_Variance_Hours = F16(VocAlgorithm_TAU_MEAN_VARIANCE_HOURS);
|
||||
params->mGating_Max_Duration_Minutes =
|
||||
F16(VocAlgorithm_GATING_MAX_DURATION_MINUTES);
|
||||
params->mSraw_Std_Initial = F16(VocAlgorithm_SRAW_STD_INITIAL);
|
||||
params->mUptime = F16(0.);
|
||||
params->mSraw = F16(0.);
|
||||
params->mVoc_Index = 0;
|
||||
VocAlgorithm__init_instances(params);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__init_instances(VocAlgorithmParams* params) {
|
||||
VocAlgorithm__mean_variance_estimator__init(params);
|
||||
VocAlgorithm__mean_variance_estimator__set_parameters(
|
||||
params, params->mSraw_Std_Initial, params->mTau_Mean_Variance_Hours,
|
||||
params->mGating_Max_Duration_Minutes);
|
||||
VocAlgorithm__mox_model__init(params);
|
||||
VocAlgorithm__mox_model__set_parameters(
|
||||
params, VocAlgorithm__mean_variance_estimator__get_std(params),
|
||||
VocAlgorithm__mean_variance_estimator__get_mean(params));
|
||||
VocAlgorithm__sigmoid_scaled__init(params);
|
||||
VocAlgorithm__sigmoid_scaled__set_parameters(params,
|
||||
params->mVoc_Index_Offset);
|
||||
VocAlgorithm__adaptive_lowpass__init(params);
|
||||
VocAlgorithm__adaptive_lowpass__set_parameters(params);
|
||||
}
|
||||
|
||||
void VocAlgorithm_get_states(VocAlgorithmParams* params, int32_t* state0,
|
||||
int32_t* state1) {
|
||||
*state0 = VocAlgorithm__mean_variance_estimator__get_mean(params);
|
||||
*state1 = VocAlgorithm__mean_variance_estimator__get_std(params);
|
||||
return;
|
||||
}
|
||||
|
||||
void VocAlgorithm_set_states(VocAlgorithmParams* params, int32_t state0,
|
||||
int32_t state1) {
|
||||
VocAlgorithm__mean_variance_estimator__set_states(
|
||||
params, state0, state1, F16(VocAlgorithm_PERSISTENCE_UPTIME_GAMMA));
|
||||
params->mSraw = state0;
|
||||
}
|
||||
|
||||
void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams* params,
|
||||
int32_t voc_index_offset,
|
||||
int32_t learning_time_hours,
|
||||
int32_t gating_max_duration_minutes,
|
||||
int32_t std_initial) {
|
||||
params->mVoc_Index_Offset = (fix16_from_int(voc_index_offset));
|
||||
params->mTau_Mean_Variance_Hours = (fix16_from_int(learning_time_hours));
|
||||
params->mGating_Max_Duration_Minutes =
|
||||
(fix16_from_int(gating_max_duration_minutes));
|
||||
params->mSraw_Std_Initial = (fix16_from_int(std_initial));
|
||||
VocAlgorithm__init_instances(params);
|
||||
}
|
||||
|
||||
void VocAlgorithm_process(VocAlgorithmParams* params, int32_t sraw,
|
||||
int32_t* voc_index) {
|
||||
if ((params->mUptime <= F16(VocAlgorithm_INITIAL_BLACKOUT))) {
|
||||
params->mUptime = (params->mUptime + F16(VocAlgorithm_SAMPLING_INTERVAL));
|
||||
} else {
|
||||
if (((sraw > 0) && (sraw < 65000))) {
|
||||
if ((sraw < 20001)) {
|
||||
sraw = 20001;
|
||||
} else if ((sraw > 52767)) {
|
||||
sraw = 52767;
|
||||
}
|
||||
params->mSraw = (fix16_from_int((sraw - 20000)));
|
||||
}
|
||||
params->mVoc_Index =
|
||||
VocAlgorithm__mox_model__process(params, params->mSraw);
|
||||
params->mVoc_Index =
|
||||
VocAlgorithm__sigmoid_scaled__process(params, params->mVoc_Index);
|
||||
params->mVoc_Index =
|
||||
VocAlgorithm__adaptive_lowpass__process(params, params->mVoc_Index);
|
||||
if ((params->mVoc_Index < F16(0.5))) {
|
||||
params->mVoc_Index = F16(0.5);
|
||||
}
|
||||
if ((params->mSraw > F16(0.))) {
|
||||
VocAlgorithm__mean_variance_estimator__process(params, params->mSraw,
|
||||
params->mVoc_Index);
|
||||
VocAlgorithm__mox_model__set_parameters(
|
||||
params, VocAlgorithm__mean_variance_estimator__get_std(params),
|
||||
VocAlgorithm__mean_variance_estimator__get_mean(params));
|
||||
}
|
||||
}
|
||||
*voc_index = (fix16_cast_to_int((params->mVoc_Index + F16(0.5))));
|
||||
return;
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator__init(
|
||||
VocAlgorithmParams* params) {
|
||||
VocAlgorithm__mean_variance_estimator__set_parameters(params, F16(0.),
|
||||
F16(0.), F16(0.));
|
||||
VocAlgorithm__mean_variance_estimator___init_instances(params);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator___init_instances(
|
||||
VocAlgorithmParams* params) {
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__init(params);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t std_initial,
|
||||
fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes) {
|
||||
params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes =
|
||||
gating_max_duration_minutes;
|
||||
params->m_Mean_Variance_Estimator___Initialized = false;
|
||||
params->m_Mean_Variance_Estimator___Mean = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Sraw_Offset = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Std = std_initial;
|
||||
params->m_Mean_Variance_Estimator___Gamma =
|
||||
(fix16_div(F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
|
||||
(VocAlgorithm_SAMPLING_INTERVAL / 3600.))),
|
||||
(tau_mean_variance_hours +
|
||||
F16((VocAlgorithm_SAMPLING_INTERVAL / 3600.)))));
|
||||
params->m_Mean_Variance_Estimator___Gamma_Initial_Mean =
|
||||
F16(((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
|
||||
VocAlgorithm_SAMPLING_INTERVAL) /
|
||||
(VocAlgorithm_TAU_INITIAL_MEAN + VocAlgorithm_SAMPLING_INTERVAL)));
|
||||
params->m_Mean_Variance_Estimator___Gamma_Initial_Variance = F16(
|
||||
((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
|
||||
VocAlgorithm_SAMPLING_INTERVAL) /
|
||||
(VocAlgorithm_TAU_INITIAL_VARIANCE + VocAlgorithm_SAMPLING_INTERVAL)));
|
||||
params->m_Mean_Variance_Estimator__Gamma_Mean = F16(0.);
|
||||
params->m_Mean_Variance_Estimator__Gamma_Variance = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gamma = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
|
||||
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator__set_states(
|
||||
VocAlgorithmParams* params, fix16_t mean, fix16_t std,
|
||||
fix16_t uptime_gamma) {
|
||||
params->m_Mean_Variance_Estimator___Mean = mean;
|
||||
params->m_Mean_Variance_Estimator___Std = std;
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gamma = uptime_gamma;
|
||||
params->m_Mean_Variance_Estimator___Initialized = true;
|
||||
}
|
||||
|
||||
static fix16_t VocAlgorithm__mean_variance_estimator__get_std(
|
||||
VocAlgorithmParams* params) {
|
||||
return params->m_Mean_Variance_Estimator___Std;
|
||||
}
|
||||
|
||||
static fix16_t VocAlgorithm__mean_variance_estimator__get_mean(
|
||||
VocAlgorithmParams* params) {
|
||||
return (params->m_Mean_Variance_Estimator___Mean +
|
||||
params->m_Mean_Variance_Estimator___Sraw_Offset);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
|
||||
VocAlgorithmParams* params, fix16_t voc_index_from_prior) {
|
||||
fix16_t uptime_limit;
|
||||
fix16_t sigmoid_gamma_mean;
|
||||
fix16_t gamma_mean;
|
||||
fix16_t gating_threshold_mean;
|
||||
fix16_t sigmoid_gating_mean;
|
||||
fix16_t sigmoid_gamma_variance;
|
||||
fix16_t gamma_variance;
|
||||
fix16_t gating_threshold_variance;
|
||||
fix16_t sigmoid_gating_variance;
|
||||
|
||||
uptime_limit = F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX -
|
||||
VocAlgorithm_SAMPLING_INTERVAL));
|
||||
if ((params->m_Mean_Variance_Estimator___Uptime_Gamma < uptime_limit)) {
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gamma =
|
||||
(params->m_Mean_Variance_Estimator___Uptime_Gamma +
|
||||
F16(VocAlgorithm_SAMPLING_INTERVAL));
|
||||
}
|
||||
if ((params->m_Mean_Variance_Estimator___Uptime_Gating < uptime_limit)) {
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gating =
|
||||
(params->m_Mean_Variance_Estimator___Uptime_Gating +
|
||||
F16(VocAlgorithm_SAMPLING_INTERVAL));
|
||||
}
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(1.), F16(VocAlgorithm_INIT_DURATION_MEAN),
|
||||
F16(VocAlgorithm_INIT_TRANSITION_MEAN));
|
||||
sigmoid_gamma_mean = VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
|
||||
gamma_mean =
|
||||
(params->m_Mean_Variance_Estimator___Gamma +
|
||||
(fix16_mul((params->m_Mean_Variance_Estimator___Gamma_Initial_Mean -
|
||||
params->m_Mean_Variance_Estimator___Gamma),
|
||||
sigmoid_gamma_mean)));
|
||||
gating_threshold_mean =
|
||||
(F16(VocAlgorithm_GATING_THRESHOLD) +
|
||||
(fix16_mul(
|
||||
F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
|
||||
VocAlgorithm_GATING_THRESHOLD)),
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(1.), gating_threshold_mean,
|
||||
F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
|
||||
sigmoid_gating_mean =
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, voc_index_from_prior);
|
||||
params->m_Mean_Variance_Estimator__Gamma_Mean =
|
||||
(fix16_mul(sigmoid_gating_mean, gamma_mean));
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(1.), F16(VocAlgorithm_INIT_DURATION_VARIANCE),
|
||||
F16(VocAlgorithm_INIT_TRANSITION_VARIANCE));
|
||||
sigmoid_gamma_variance =
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
|
||||
gamma_variance =
|
||||
(params->m_Mean_Variance_Estimator___Gamma +
|
||||
(fix16_mul((params->m_Mean_Variance_Estimator___Gamma_Initial_Variance -
|
||||
params->m_Mean_Variance_Estimator___Gamma),
|
||||
(sigmoid_gamma_variance - sigmoid_gamma_mean))));
|
||||
gating_threshold_variance =
|
||||
(F16(VocAlgorithm_GATING_THRESHOLD) +
|
||||
(fix16_mul(
|
||||
F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
|
||||
VocAlgorithm_GATING_THRESHOLD)),
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(1.), gating_threshold_variance,
|
||||
F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
|
||||
sigmoid_gating_variance =
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
params, voc_index_from_prior);
|
||||
params->m_Mean_Variance_Estimator__Gamma_Variance =
|
||||
(fix16_mul(sigmoid_gating_variance, gamma_variance));
|
||||
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes =
|
||||
(params->m_Mean_Variance_Estimator___Gating_Duration_Minutes +
|
||||
(fix16_mul(F16((VocAlgorithm_SAMPLING_INTERVAL / 60.)),
|
||||
((fix16_mul((F16(1.) - sigmoid_gating_mean),
|
||||
F16((1. + VocAlgorithm_GATING_MAX_RATIO)))) -
|
||||
F16(VocAlgorithm_GATING_MAX_RATIO)))));
|
||||
if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes < F16(0.))) {
|
||||
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
|
||||
}
|
||||
if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes >
|
||||
params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes)) {
|
||||
params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
|
||||
}
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator__process(
|
||||
VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior) {
|
||||
fix16_t delta_sgp;
|
||||
fix16_t c;
|
||||
fix16_t additional_scaling;
|
||||
|
||||
if ((params->m_Mean_Variance_Estimator___Initialized == false)) {
|
||||
params->m_Mean_Variance_Estimator___Initialized = true;
|
||||
params->m_Mean_Variance_Estimator___Sraw_Offset = sraw;
|
||||
params->m_Mean_Variance_Estimator___Mean = F16(0.);
|
||||
} else {
|
||||
if (((params->m_Mean_Variance_Estimator___Mean >= F16(100.)) ||
|
||||
(params->m_Mean_Variance_Estimator___Mean <= F16(-100.)))) {
|
||||
params->m_Mean_Variance_Estimator___Sraw_Offset =
|
||||
(params->m_Mean_Variance_Estimator___Sraw_Offset +
|
||||
params->m_Mean_Variance_Estimator___Mean);
|
||||
params->m_Mean_Variance_Estimator___Mean = F16(0.);
|
||||
}
|
||||
sraw = (sraw - params->m_Mean_Variance_Estimator___Sraw_Offset);
|
||||
VocAlgorithm__mean_variance_estimator___calculate_gamma(
|
||||
params, voc_index_from_prior);
|
||||
delta_sgp =
|
||||
(fix16_div((sraw - params->m_Mean_Variance_Estimator___Mean),
|
||||
F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING)));
|
||||
if ((delta_sgp < F16(0.))) {
|
||||
c = (params->m_Mean_Variance_Estimator___Std - delta_sgp);
|
||||
} else {
|
||||
c = (params->m_Mean_Variance_Estimator___Std + delta_sgp);
|
||||
}
|
||||
additional_scaling = F16(1.);
|
||||
if ((c > F16(1440.))) {
|
||||
additional_scaling = F16(4.);
|
||||
}
|
||||
params->m_Mean_Variance_Estimator___Std = (fix16_mul(
|
||||
fix16_sqrt((fix16_mul(
|
||||
additional_scaling,
|
||||
(F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) -
|
||||
params->m_Mean_Variance_Estimator__Gamma_Variance)))),
|
||||
fix16_sqrt((
|
||||
(fix16_mul(
|
||||
params->m_Mean_Variance_Estimator___Std,
|
||||
(fix16_div(
|
||||
params->m_Mean_Variance_Estimator___Std,
|
||||
(fix16_mul(
|
||||
F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING),
|
||||
additional_scaling)))))) +
|
||||
(fix16_mul(
|
||||
(fix16_div(
|
||||
(fix16_mul(
|
||||
params->m_Mean_Variance_Estimator__Gamma_Variance,
|
||||
delta_sgp)),
|
||||
additional_scaling)),
|
||||
delta_sgp))))));
|
||||
params->m_Mean_Variance_Estimator___Mean =
|
||||
(params->m_Mean_Variance_Estimator___Mean +
|
||||
(fix16_mul(params->m_Mean_Variance_Estimator__Gamma_Mean, delta_sgp)));
|
||||
}
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
|
||||
VocAlgorithmParams* params) {
|
||||
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
params, F16(0.), F16(0.), F16(0.));
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K) {
|
||||
params->m_Mean_Variance_Estimator___Sigmoid__L = L;
|
||||
params->m_Mean_Variance_Estimator___Sigmoid__K = K;
|
||||
params->m_Mean_Variance_Estimator___Sigmoid__X0 = X0;
|
||||
}
|
||||
|
||||
static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
|
||||
VocAlgorithmParams* params, fix16_t sample) {
|
||||
fix16_t x;
|
||||
|
||||
x = (fix16_mul(params->m_Mean_Variance_Estimator___Sigmoid__K,
|
||||
(sample - params->m_Mean_Variance_Estimator___Sigmoid__X0)));
|
||||
if ((x < F16(-50.))) {
|
||||
return params->m_Mean_Variance_Estimator___Sigmoid__L;
|
||||
} else if ((x > F16(50.))) {
|
||||
return F16(0.);
|
||||
} else {
|
||||
return (fix16_div(params->m_Mean_Variance_Estimator___Sigmoid__L,
|
||||
(F16(1.) + fix16_exp(x))));
|
||||
}
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params) {
|
||||
VocAlgorithm__mox_model__set_parameters(params, F16(1.), F16(0.));
|
||||
}
|
||||
|
||||
static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
|
||||
fix16_t SRAW_STD,
|
||||
fix16_t SRAW_MEAN) {
|
||||
params->m_Mox_Model__Sraw_Std = SRAW_STD;
|
||||
params->m_Mox_Model__Sraw_Mean = SRAW_MEAN;
|
||||
}
|
||||
|
||||
static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
|
||||
fix16_t sraw) {
|
||||
return (fix16_mul((fix16_div((sraw - params->m_Mox_Model__Sraw_Mean),
|
||||
(-(params->m_Mox_Model__Sraw_Std +
|
||||
F16(VocAlgorithm_SRAW_STD_BONUS))))),
|
||||
F16(VocAlgorithm_VOC_INDEX_GAIN)));
|
||||
}
|
||||
|
||||
static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params) {
|
||||
VocAlgorithm__sigmoid_scaled__set_parameters(params, F16(0.));
|
||||
}
|
||||
|
||||
static void VocAlgorithm__sigmoid_scaled__set_parameters(
|
||||
VocAlgorithmParams* params, fix16_t offset) {
|
||||
params->m_Sigmoid_Scaled__Offset = offset;
|
||||
}
|
||||
|
||||
static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
|
||||
fix16_t sample) {
|
||||
fix16_t x;
|
||||
fix16_t shift;
|
||||
|
||||
x = (fix16_mul(F16(VocAlgorithm_SIGMOID_K),
|
||||
(sample - F16(VocAlgorithm_SIGMOID_X0))));
|
||||
if ((x < F16(-50.))) {
|
||||
return F16(VocAlgorithm_SIGMOID_L);
|
||||
} else if ((x > F16(50.))) {
|
||||
return F16(0.);
|
||||
} else {
|
||||
if ((sample >= F16(0.))) {
|
||||
shift =
|
||||
(fix16_div((F16(VocAlgorithm_SIGMOID_L) -
|
||||
(fix16_mul(F16(5.), params->m_Sigmoid_Scaled__Offset))),
|
||||
F16(4.)));
|
||||
return ((fix16_div((F16(VocAlgorithm_SIGMOID_L) + shift),
|
||||
(F16(1.) + fix16_exp(x)))) -
|
||||
shift);
|
||||
} else {
|
||||
return (fix16_mul(
|
||||
(fix16_div(params->m_Sigmoid_Scaled__Offset,
|
||||
F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT))),
|
||||
(fix16_div(F16(VocAlgorithm_SIGMOID_L), (F16(1.) + fix16_exp(x))))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params) {
|
||||
VocAlgorithm__adaptive_lowpass__set_parameters(params);
|
||||
}
|
||||
|
||||
static void VocAlgorithm__adaptive_lowpass__set_parameters(
|
||||
VocAlgorithmParams* params) {
|
||||
params->m_Adaptive_Lowpass__A1 =
|
||||
F16((VocAlgorithm_SAMPLING_INTERVAL /
|
||||
(VocAlgorithm_LP_TAU_FAST + VocAlgorithm_SAMPLING_INTERVAL)));
|
||||
params->m_Adaptive_Lowpass__A2 =
|
||||
F16((VocAlgorithm_SAMPLING_INTERVAL /
|
||||
(VocAlgorithm_LP_TAU_SLOW + VocAlgorithm_SAMPLING_INTERVAL)));
|
||||
params->m_Adaptive_Lowpass___Initialized = false;
|
||||
}
|
||||
|
||||
static fix16_t VocAlgorithm__adaptive_lowpass__process(
|
||||
VocAlgorithmParams* params, fix16_t sample) {
|
||||
fix16_t abs_delta;
|
||||
fix16_t F1;
|
||||
fix16_t tau_a;
|
||||
fix16_t a3;
|
||||
|
||||
if ((params->m_Adaptive_Lowpass___Initialized == false)) {
|
||||
params->m_Adaptive_Lowpass___X1 = sample;
|
||||
params->m_Adaptive_Lowpass___X2 = sample;
|
||||
params->m_Adaptive_Lowpass___X3 = sample;
|
||||
params->m_Adaptive_Lowpass___Initialized = true;
|
||||
}
|
||||
params->m_Adaptive_Lowpass___X1 =
|
||||
((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A1),
|
||||
params->m_Adaptive_Lowpass___X1)) +
|
||||
(fix16_mul(params->m_Adaptive_Lowpass__A1, sample)));
|
||||
params->m_Adaptive_Lowpass___X2 =
|
||||
((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A2),
|
||||
params->m_Adaptive_Lowpass___X2)) +
|
||||
(fix16_mul(params->m_Adaptive_Lowpass__A2, sample)));
|
||||
abs_delta =
|
||||
(params->m_Adaptive_Lowpass___X1 - params->m_Adaptive_Lowpass___X2);
|
||||
if ((abs_delta < F16(0.))) {
|
||||
abs_delta = (-abs_delta);
|
||||
}
|
||||
F1 = fix16_exp((fix16_mul(F16(VocAlgorithm_LP_ALPHA), abs_delta)));
|
||||
tau_a =
|
||||
((fix16_mul(F16((VocAlgorithm_LP_TAU_SLOW - VocAlgorithm_LP_TAU_FAST)),
|
||||
F1)) +
|
||||
F16(VocAlgorithm_LP_TAU_FAST));
|
||||
a3 = (fix16_div(F16(VocAlgorithm_SAMPLING_INTERVAL),
|
||||
(F16(VocAlgorithm_SAMPLING_INTERVAL) + tau_a)));
|
||||
params->m_Adaptive_Lowpass___X3 =
|
||||
((fix16_mul((F16(1.) - a3), params->m_Adaptive_Lowpass___X3)) +
|
||||
(fix16_mul(a3, sample)));
|
||||
return params->m_Adaptive_Lowpass___X3;
|
||||
}
|
||||
+9
-9
@@ -40,8 +40,8 @@
|
||||
|
||||
typedef int32_t fix16_t;
|
||||
|
||||
#define F16(x) \
|
||||
((fix16_t)(((x) >= 0) ? ((x)*65536.0 + 0.5) : ((x)*65536.0 - 0.5)))
|
||||
#define F16(x) \
|
||||
((fix16_t)(((x) >= 0) ? ((x) * 65536.0 + 0.5) : ((x) * 65536.0 - 0.5)))
|
||||
|
||||
#define VocAlgorithm_SAMPLING_INTERVAL (1.)
|
||||
#define VocAlgorithm_INITIAL_BLACKOUT (45.)
|
||||
@@ -114,7 +114,7 @@ typedef struct {
|
||||
* whenever the sensor stopped measurements.
|
||||
* @param params Pointer to the VocAlgorithmParams struct
|
||||
*/
|
||||
void VocAlgorithm_init(VocAlgorithmParams *params);
|
||||
void VocAlgorithm_init(VocAlgorithmParams* params);
|
||||
|
||||
/**
|
||||
* Get current algorithm states. Retrieved values can be used in
|
||||
@@ -125,8 +125,8 @@ void VocAlgorithm_init(VocAlgorithmParams *params);
|
||||
* @param state0 State0 to be stored
|
||||
* @param state1 State1 to be stored
|
||||
*/
|
||||
void VocAlgorithm_get_states(VocAlgorithmParams *params, int32_t *state0,
|
||||
int32_t *state1);
|
||||
void VocAlgorithm_get_states(VocAlgorithmParams* params, int32_t* state0,
|
||||
int32_t* state1);
|
||||
|
||||
/**
|
||||
* Set previously retrieved algorithm states to resume operation after a short
|
||||
@@ -138,7 +138,7 @@ void VocAlgorithm_get_states(VocAlgorithmParams *params, int32_t *state0,
|
||||
* @param state0 State0 to be restored
|
||||
* @param state1 State1 to be restored
|
||||
*/
|
||||
void VocAlgorithm_set_states(VocAlgorithmParams *params, int32_t state0,
|
||||
void VocAlgorithm_set_states(VocAlgorithmParams* params, int32_t state0,
|
||||
int32_t state1);
|
||||
|
||||
/**
|
||||
@@ -162,7 +162,7 @@ void VocAlgorithm_set_states(VocAlgorithmParams *params, int32_t state0,
|
||||
* device-to-device variations.
|
||||
* Range 10..500, default 50
|
||||
*/
|
||||
void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams *params,
|
||||
void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams* params,
|
||||
int32_t voc_index_offset,
|
||||
int32_t learning_time_hours,
|
||||
int32_t gating_max_duration_minutes,
|
||||
@@ -176,7 +176,7 @@ void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams *params,
|
||||
* @param voc_index Calculated VOC index value from the raw sensor value. Zero
|
||||
* during initial blackout period and 1..500 afterwards
|
||||
*/
|
||||
void VocAlgorithm_process(VocAlgorithmParams *params, int32_t sraw,
|
||||
int32_t *voc_index);
|
||||
void VocAlgorithm_process(VocAlgorithmParams* params, int32_t sraw,
|
||||
int32_t* voc_index);
|
||||
|
||||
#endif /* VOCALGORITHM_H_ */
|
||||
@@ -348,18 +348,19 @@ void DS3231Detected(void) {
|
||||
* Read time and return the epoch time (second since 1-1-1970 00:00)
|
||||
\*-------------------------------------------------------------------------------------------*/
|
||||
uint32_t Pcf85063ReadTime(void) {
|
||||
Wire.beginTransmission(RtcChip.address);
|
||||
Wire.write(PCF85063_REG_SECONDS);
|
||||
Wire.endTransmission(false); // false -> repeated start
|
||||
Wire.requestFrom((uint8_t)RtcChip.address, (uint8_t)7);
|
||||
TwoWire& myWire = I2cGetWire(RtcChip.bus);
|
||||
myWire.beginTransmission(RtcChip.address);
|
||||
myWire.write(PCF85063_REG_SECONDS);
|
||||
myWire.endTransmission(false); // false -> repeated start
|
||||
myWire.requestFrom((uint8_t)RtcChip.address, (uint8_t)7);
|
||||
|
||||
uint8_t sec = Wire.read(); // 0x04
|
||||
uint8_t min = Wire.read(); // 0x05
|
||||
uint8_t hour = Wire.read(); // 0x06
|
||||
uint8_t day = Wire.read(); // 0x07
|
||||
uint8_t wday = Wire.read(); // 0x08
|
||||
uint8_t month = Wire.read(); // 0x09
|
||||
uint8_t year = Wire.read(); // 0x0A
|
||||
uint8_t sec = myWire.read(); // 0x04
|
||||
uint8_t min = myWire.read(); // 0x05
|
||||
uint8_t hour = myWire.read(); // 0x06
|
||||
uint8_t day = myWire.read(); // 0x07
|
||||
uint8_t wday = myWire.read(); // 0x08
|
||||
uint8_t month = myWire.read(); // 0x09
|
||||
uint8_t year = myWire.read(); // 0x0A
|
||||
|
||||
TIME_T tm;
|
||||
tm.second = Bcd2Dec(sec & 0x7F);
|
||||
@@ -391,16 +392,17 @@ void Pcf85063SetTime(uint32_t epoch_time) {
|
||||
uint8_t bcd_month = Dec2Bcd(tm.month +1);
|
||||
uint8_t bcd_year = Dec2Bcd(year);
|
||||
|
||||
Wire.beginTransmission(RtcChip.address);
|
||||
Wire.write(PCF85063_REG_SECONDS);
|
||||
Wire.write(bcd_sec);
|
||||
Wire.write(bcd_min);
|
||||
Wire.write(bcd_hour);
|
||||
Wire.write(bcd_day);
|
||||
Wire.write(bcd_wday);
|
||||
Wire.write(bcd_month);
|
||||
Wire.write(bcd_year);
|
||||
Wire.endTransmission();
|
||||
TwoWire& myWire = I2cGetWire(RtcChip.bus);
|
||||
myWire.beginTransmission(RtcChip.address);
|
||||
myWire.write(PCF85063_REG_SECONDS);
|
||||
myWire.write(bcd_sec);
|
||||
myWire.write(bcd_min);
|
||||
myWire.write(bcd_hour);
|
||||
myWire.write(bcd_day);
|
||||
myWire.write(bcd_wday);
|
||||
myWire.write(bcd_month);
|
||||
myWire.write(bcd_year);
|
||||
myWire.endTransmission();
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*\
|
||||
@@ -409,17 +411,18 @@ void Pcf85063SetTime(uint32_t epoch_time) {
|
||||
void Pcf85063Detected(void) {
|
||||
if (!RtcChip.detected && I2cEnabled(XI2C_92)) {
|
||||
RtcChip.address = PCF85063_ADDRESS;
|
||||
// Vyskúšame, či vieme prečítať nejaký register
|
||||
if (I2cSetDevice(RtcChip.address)) {
|
||||
for (RtcChip.bus = 0; RtcChip.bus < 2; RtcChip.bus++) {
|
||||
// Vyskúšame, či vieme prečítať nejaký register
|
||||
if (!I2cSetDevice(RtcChip.address, RtcChip.bus)) { continue; }
|
||||
// Skúsime napr. prečítať PCF85063_REG_CTRL1
|
||||
if (I2cValidRead(RtcChip.address, PCF85063_REG_CTRL1, 1)) {
|
||||
if (I2cValidRead(RtcChip.address, PCF85063_REG_CTRL1, 1, RtcChip.bus)) {
|
||||
RtcChip.detected = 1;
|
||||
strcpy_P(RtcChip.name, PSTR("PCF85063"));
|
||||
RtcChip.ReadTime = &Pcf85063ReadTime;
|
||||
RtcChip.SetTime = &Pcf85063SetTime;
|
||||
RtcChip.mem_size = -1; // Nemá extra user RAM, ak by si nepotreboval
|
||||
|
||||
// Ak by si chcel implementovať MemRead/MemWrite, doplň RtcChip.MemRead a RtcChip.MemWrite
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -519,14 +522,15 @@ void BM8563Detected(void) {
|
||||
* Read time and return the epoch time (second since 1-1-1970 00:00)
|
||||
\*-------------------------------------------------------------------------------------------*/
|
||||
uint32_t Pcf85363ReadTime(void) {
|
||||
Wire.beginTransmission(RtcChip.address);
|
||||
Wire.write(0x00);
|
||||
Wire.endTransmission();
|
||||
TwoWire& myWire = I2cGetWire(RtcChip.bus);
|
||||
myWire.beginTransmission(RtcChip.address);
|
||||
myWire.write(0x00);
|
||||
myWire.endTransmission();
|
||||
|
||||
uint8_t buffer[8];
|
||||
Wire.requestFrom(RtcChip.address, (uint8_t)8);
|
||||
for (uint32_t i = 0; i < 8; i++) { buffer[i] = Wire.read(); }
|
||||
Wire.endTransmission();
|
||||
myWire.requestFrom(RtcChip.address, (uint8_t)8);
|
||||
for (uint32_t i = 0; i < 8; i++) { buffer[i] = myWire.read(); }
|
||||
myWire.endTransmission();
|
||||
|
||||
TIME_T tm;
|
||||
tm.second = Bcd2Dec(buffer[1] & 0x7F);
|
||||
@@ -555,23 +559,25 @@ void Pcf85363SetTime(uint32_t epoch_time) {
|
||||
buffer[5] = tm.day_of_week;
|
||||
buffer[6] = Dec2Bcd(tm.month);
|
||||
buffer[7] = Dec2Bcd(tm.year -30); // Offset from 1970
|
||||
|
||||
TwoWire& myWire = I2cGetWire(RtcChip.bus);
|
||||
/*
|
||||
// Handbook page 13
|
||||
Wire.beginTransmission(RtcChip.address);
|
||||
Wire.write(0x2E);
|
||||
Wire.write(0x01); // Set stop
|
||||
Wire.write(0xA4); // Clear prescaler
|
||||
for (uint32_t i = 0; i < 8; i++) { Wire.write(buffer[i]); }
|
||||
Wire.endTransmission();
|
||||
Wire.beginTransmission(RtcChip.address);
|
||||
Wire.write(0x2E);
|
||||
Wire.write(0x00); // Set start
|
||||
Wire.endTransmission();
|
||||
myWire.beginTransmission(RtcChip.address);
|
||||
myWire.write(0x2E);
|
||||
myWire.write(0x01); // Set stop
|
||||
myWire.write(0xA4); // Clear prescaler
|
||||
for (uint32_t i = 0; i < 8; i++) { myWire.write(buffer[i]); }
|
||||
myWire.endTransmission();
|
||||
myWire.beginTransmission(RtcChip.address);
|
||||
myWire.write(0x2E);
|
||||
myWire.write(0x00); // Set start
|
||||
myWire.endTransmission();
|
||||
*/
|
||||
Wire.beginTransmission(RtcChip.address);
|
||||
Wire.write(0x00);
|
||||
for (uint32_t i = 0; i < 8; i++) { Wire.write(buffer[i]); }
|
||||
Wire.endTransmission();
|
||||
myWire.beginTransmission(RtcChip.address);
|
||||
myWire.write(0x00);
|
||||
for (uint32_t i = 0; i < 8; i++) { myWire.write(buffer[i]); }
|
||||
myWire.endTransmission();
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*\
|
||||
@@ -581,26 +587,27 @@ void Pcf85363SetTime(uint32_t epoch_time) {
|
||||
void Pcf85363Dump(void) {
|
||||
uint8_t buffer[64];
|
||||
|
||||
TwoWire& myWire = I2cGetWire(RtcChip.bus);
|
||||
// 0x00 to 0x2F
|
||||
Wire.beginTransmission(RtcChip.address);
|
||||
Wire.write(0x00);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom(RtcChip.address, (uint8_t)48);
|
||||
myWire.beginTransmission(RtcChip.address);
|
||||
myWire.write(0x00);
|
||||
myWire.endTransmission();
|
||||
myWire.requestFrom(RtcChip.address, (uint8_t)48);
|
||||
for (uint32_t i = 0; i < 48; i++) {
|
||||
buffer[i] = Wire.read();
|
||||
buffer[i] = myWire.read();
|
||||
}
|
||||
Wire.endTransmission();
|
||||
myWire.endTransmission();
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("P85: Read 0x00: %48_H"), buffer);
|
||||
|
||||
// 0x40 to 0x7F
|
||||
Wire.beginTransmission(RtcChip.address);
|
||||
Wire.write(0x40);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom(RtcChip.address, (uint8_t)64);
|
||||
myWire.beginTransmission(RtcChip.address);
|
||||
myWire.write(0x40);
|
||||
myWire.endTransmission();
|
||||
myWire.requestFrom(RtcChip.address, (uint8_t)64);
|
||||
for (uint32_t i = 0; i < 64; i++) {
|
||||
buffer[i] = Wire.read();
|
||||
buffer[i] = myWire.read();
|
||||
}
|
||||
Wire.endTransmission();
|
||||
myWire.endTransmission();
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("P85: Read 0x40: %64_H"), buffer);
|
||||
}
|
||||
*/
|
||||
@@ -609,11 +616,11 @@ void Pcf85363Dump(void) {
|
||||
* Memory block functions
|
||||
\*-------------------------------------------------------------------------------------------*/
|
||||
int32_t Pcf8563MemRead(uint8_t *buffer, uint32_t size) {
|
||||
return I2cReadBuffer(RtcChip.address, 0x40, buffer, size);
|
||||
return I2cReadBuffer(RtcChip.address, 0x40, buffer, size, RtcChip.bus);
|
||||
}
|
||||
|
||||
int32_t Pcf8563MemWrite(uint8_t *buffer, uint32_t size) {
|
||||
return I2cWriteBuffer(RtcChip.address, 0x40, (uint8_t *)buffer, size);
|
||||
return I2cWriteBuffer(RtcChip.address, 0x40, (uint8_t *)buffer, size, RtcChip.bus);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*\
|
||||
@@ -622,7 +629,8 @@ int32_t Pcf8563MemWrite(uint8_t *buffer, uint32_t size) {
|
||||
void Pcf85363Detected(void) {
|
||||
if (!RtcChip.detected && I2cEnabled(XI2C_66)) {
|
||||
RtcChip.address = PCF85363_ADDRESS;
|
||||
if (I2cSetDevice(RtcChip.address)) {
|
||||
for (RtcChip.bus = 0; RtcChip.bus < 2; RtcChip.bus++) {
|
||||
if (!I2cSetDevice(RtcChip.address, RtcChip.bus)) { continue; }
|
||||
RtcChip.detected = 1;
|
||||
strcpy_P(RtcChip.name, PSTR("PCF85363"));
|
||||
RtcChip.ReadTime = &Pcf85363ReadTime;
|
||||
|
||||
@@ -64,15 +64,15 @@ bool Sen5xError(const char* func, int error) {
|
||||
void sen5x_Init(void) {
|
||||
PowerOnDelay(60); // Sensor startup time (Time after power-on until I2C communication can be started)
|
||||
for (uint32_t bus = 0; bus < 2; bus++) {
|
||||
if (!I2cSetDevice(SEN5X_ADDRESS, bus)) {
|
||||
continue;
|
||||
}
|
||||
if (!I2cSetDevice(SEN5X_ADDRESS, bus)) { continue; }
|
||||
sen5x = new SensirionI2CSen5x();
|
||||
sen5x->begin(I2cGetWire(bus));
|
||||
|
||||
if (!Settings->flag6.sen5x_passive_mode) { // SetOption156 - (Sen5x) Run in passive mode when there is another I2C master (e.g. Ikea Vindstyrka), i.e. do not set up Sen5x sensor, higher polling interval
|
||||
if (Sen5xError("Reset", sen5x->deviceReset())) { // Performs delay(200) if no error
|
||||
continue;
|
||||
if (sen5x->deviceReset()) { // Performs delay(200) if no error
|
||||
if (Sen5xError("Reset", sen5x->deviceReset())) { // See https://github.com/arendst/Tasmota/discussions/24452
|
||||
continue;
|
||||
}
|
||||
}
|
||||
delay(1100); // Wait 1 second for sensors to start recording + 100ms for reset command
|
||||
if (Sen5xError("Measurement", sen5x->startMeasurement())) {
|
||||
|
||||
@@ -44,12 +44,16 @@ float sgp30_abshum;
|
||||
void sgp30_Init(void) {
|
||||
for (uint32_t bus = 0; bus < 2; bus++) {
|
||||
if (!I2cSetDevice(SGP30_ADDRESS, bus)) { continue; }
|
||||
if (sgp.begin(&I2cGetWire(bus))) {
|
||||
sgp30_type = true;
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("SGP: Serialnumber 0x%04X-0x%04X-0x%04X"), sgp.serialnumber[0], sgp.serialnumber[1], sgp.serialnumber[2]);
|
||||
I2cSetActiveFound(SGP30_ADDRESS, "SGP30", bus);
|
||||
return;
|
||||
if (!sgp.begin(&I2cGetWire(bus))) {
|
||||
if (!sgp.IAQinit()) { continue; } // Fix I2C bus incompatibilities
|
||||
if (!sgp.begin(&I2cGetWire(bus))) { continue; }
|
||||
}
|
||||
sgp30_type = true;
|
||||
I2cSetActiveFound(SGP30_ADDRESS, "SGP30", bus);
|
||||
|
||||
uint64_t serialnumber = (uint64_t)sgp.serialnumber[0] << 32 | sgp.serialnumber[1] << 16 | sgp.serialnumber[2];
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("SG3: Serialnumber %_U"), &serialnumber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +102,7 @@ void Sgp30Update(void) // Perform every second to ensure proper operation of th
|
||||
uint16_t eCO2_base;
|
||||
|
||||
if (!sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) return; // Failed to get baseline readings
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("SGP: Baseline values eCO2 0x%04X, TVOC 0x%04X"), eCO2_base, TVOC_base);
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("SG3: Baseline values eCO2 0x%04X, TVOC 0x%04X"), eCO2_base, TVOC_base);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,13 +44,13 @@ uint8_t ecnt = 0;
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void CCS811Detect(void)
|
||||
{
|
||||
if (!I2cSetDevice(CCS811_ADDRESS)) { return; }
|
||||
|
||||
if (!ccs.begin(CCS811_ADDRESS)) {
|
||||
void CCS811Detect(void) {
|
||||
for (uint32_t bus = 0; bus < 2; bus++) {
|
||||
if (!I2cSetDevice(CCS811_ADDRESS, bus)) { continue; }
|
||||
if (!ccs.begin(CCS811_ADDRESS, &I2cGetWire(bus))) { continue; }
|
||||
CCS811_type = 1;
|
||||
I2cSetActiveFound(CCS811_ADDRESS, "CCS811");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,14 +44,16 @@ int32_t voc_index;
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void sgp40_Init(void)
|
||||
{
|
||||
if (!I2cSetDevice(SGP40_ADDRESS)) { return; }
|
||||
|
||||
if (sgp40.begin()) {
|
||||
void sgp40_Init(void) {
|
||||
for (uint32_t bus = 0; bus < 2; bus++) {
|
||||
if (!I2cSetDevice(SGP40_ADDRESS, bus)) { continue; }
|
||||
if (!sgp40.begin(&I2cGetWire(bus))) { continue; }
|
||||
sgp40_type = true;
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("SGP: Serialnumber 0x%04X-0x%04X-0x%04X"), sgp40.serialnumber[0], sgp40.serialnumber[1], sgp40.serialnumber[2]);
|
||||
I2cSetActiveFound(SGP40_ADDRESS, "SGP40");
|
||||
|
||||
uint64_t serialnumber = (uint64_t)sgp40.serialnumber[0] << 32 | sgp40.serialnumber[1] << 16 | sgp40.serialnumber[2];
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("SG4: Serialnumber %_U"), &serialnumber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user