Add support for second I2C bus to some sensors

This commit is contained in:
Theo Arends
2026-03-11 12:35:25 +01:00
parent a932b3d991
commit 783d18607f
73 changed files with 2993 additions and 2036 deletions
+6
View File
@@ -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
View File
@@ -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
+6
View File
@@ -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
@@ -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;
}
@@ -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
@@ -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;
@@ -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);
@@ -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
@@ -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 [![Build Status](https://github.com/adafruit/Adafruit_BusIO/workflows/Arduino%20Library%20CI/badge.svg)](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 = .
@@ -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); }
@@ -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); }
@@ -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() {}
@@ -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() {}
@@ -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,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);
}
}
@@ -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,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 [![Build Status](https://travis-ci.org/adafruit/Adafruit_CCS811.svg?branch=master)](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 [![Build Status](https://github.com/adafruit/Adafruit_CCS811/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_CCS811/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](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.
@@ -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);
}
}
@@ -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)
@@ -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);
@@ -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
@@ -1,4 +1,4 @@
# Adafruit SGP30 Gas / Air Quality I2C sensor [[![Build Status](https://github.com/adafruit/Adafruit_SGP30/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_SGP30/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_SGP30/html/index.html)
# Adafruit SGP30 Gas / Air Quality I2C sensor [![Build Status](https://github.com/adafruit/Adafruit_SGP30/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_SGP30/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](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 dont 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 communitys 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.
@@ -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()){
@@ -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 dont 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 communitys 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,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
@@ -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;
@@ -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;
}
@@ -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())) {
+10 -6
View File
@@ -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;
}
}