From f8e7a564f102334d9ecf8139cd10f2b156a2da76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Fri, 15 Dec 2017 14:17:32 +0100 Subject: [PATCH] Support for SI7021 sensor, I2C enabled by default using Wire library --- code/espurna/config/general.h | 19 +++- code/espurna/config/hardware.h | 4 + code/espurna/config/sensors.h | 17 ++- code/espurna/espurna.ino | 5 + code/espurna/i2c.ino | 51 +++++++-- code/espurna/power_emon.ino | 50 ++++++--- code/espurna/sensor.ino | 7 +- code/espurna/sensors/DallasSensor.h | 2 +- code/espurna/sensors/SI7021Sensor.h | 159 ++++++++++++++++++++++++++++ 9 files changed, 282 insertions(+), 32 deletions(-) create mode 100644 code/espurna/sensors/SI7021Sensor.h diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 1556159a..7d1c6313 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -664,13 +664,22 @@ PROGMEM const char* const custom_reset_string[] = { // ----------------------------------------------------------------------------- #ifndef I2C_SUPPORT -#define I2C_SUPPORT 0 // I2C enabled (1.98Kb) +#define I2C_SUPPORT 1 // I2C enabled (1.98Kb) #endif -#define I2C_SDA_PIN 4 // SDA GPIO -#define I2C_SCL_PIN 14 // SCL GPIO -#define I2C_CLOCK_STRETCH_TIME 200 // BRZO clock stretch time -#define I2C_SCL_FREQUENCY 1000 // BRZO SCL frequency +#define I2C_USE_BRZO 0 // Use brzo_i2c library or standard Wire + +#ifndef I2C_SDA_PIN +#define I2C_SDA_PIN SDA // SDA GPIO (Sonoff => 4) +#endif + +#ifndef I2C_SCL_PIN +#define I2C_SCL_PIN SCL // SCL GPIO (Sonoff => 14) +#endif + +#define I2C_CLOCK_STRETCH_TIME 50000 // BRZO clock stretch time +#define I2C_SCL_FREQUENCY 200 // BRZO SCL frequency +#define I2C_CLEAR_BUS 0 // Clear I2C bus at boot // ----------------------------------------------------------------------------- // DOMOTICZ diff --git a/code/espurna/config/hardware.h b/code/espurna/config/hardware.h index 47d04220..86a55010 100644 --- a/code/espurna/config/hardware.h +++ b/code/espurna/config/hardware.h @@ -222,6 +222,10 @@ #define LED1_PIN 13 #define LED1_PIN_INVERSE 1 + // Jack is connected to GPIO14 (and with a small hack to GPIO4) + //#define I2C_SDA_PIN 4 + //#define I2C_SCL_PIN 14 + #elif defined(ITEAD_SONOFF_SV) // Info diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h index 388e64a2..12b05053 100644 --- a/code/espurna/config/sensors.h +++ b/code/espurna/config/sensors.h @@ -39,7 +39,7 @@ #endif #ifndef DHT_PIN -#define DHT_PIN 12 +#define DHT_PIN 14 #endif #ifndef DHT_TYPE @@ -115,6 +115,19 @@ #define COUNTER_DEBOUNCE 10 // Do not register events within less than 10 millis #define COUNTER_TOPIC "counter" // Default topic for MQTT, API and InfluxDB +//-------------------------------------------------------------------------------- +// SI7021 temperature & humidity sensor +// Enable support by passing SI7021_SUPPORT=1 build flag +//-------------------------------------------------------------------------------- + +#ifndef SI7021_SUPPORT +#define SI7021_SUPPORT 0 +#endif + +#ifndef SI7021_ADDRESS +#define SI7021_ADDRESS 0x40 +#endif + //-------------------------------------------------------------------------------- // DS18B20 temperature sensor // Enable support by passing DS18B20_SUPPORT=1 build flag @@ -125,7 +138,7 @@ #endif #ifndef DS18B20_PIN -#define DS18B20_PIN 13 +#define DS18B20_PIN 14 #endif #ifndef DS18B20_PULLUP diff --git a/code/espurna/espurna.ino b/code/espurna/espurna.ino index 074429cf..14d4aa7b 100644 --- a/code/espurna/espurna.ino +++ b/code/espurna/espurna.ino @@ -298,7 +298,12 @@ void setup() { #endif #if I2C_SUPPORT i2cSetup(); + #if I2C_CLEAR_BUS + i2cClearBus(); + #endif + i2cScan(); #endif + #ifdef ITEAD_SONOFF_RFBRIDGE rfbSetup(); #endif diff --git a/code/espurna/i2c.ino b/code/espurna/i2c.ino index c5ccee82..e914b35d 100644 --- a/code/espurna/i2c.ino +++ b/code/espurna/i2c.ino @@ -8,7 +8,16 @@ Copyright (C) 2017 by Xose PĂ©rez #if I2C_SUPPORT +#if I2C_USE_BRZO #include "brzo_i2c.h" +unsigned long _i2c_scl_frequency = 0; +#else +#include "Wire.h" +#endif + +// ----------------------------------------------------------------------------- +// Private +// ----------------------------------------------------------------------------- int _i2cClearbus(int sda, int scl) { @@ -93,13 +102,28 @@ int _i2cClearbus(int sda, int scl) { } +// ----------------------------------------------------------------------------- +// Utils +// ----------------------------------------------------------------------------- + +void i2cClearBus() { + unsigned char sda = getSetting("i2cSDA", I2C_SDA_PIN).toInt(); + unsigned char scl = getSetting("i2cSCL", I2C_SCL_PIN).toInt(); + DEBUG_MSG_P(PSTR("[I2C] Clear bus (response: %d)\n"), _i2cClearbus(sda, scl)); +} + bool i2cCheck(unsigned char address) { - brzo_i2c_start_transaction(address, I2C_SCL_FREQUENCY); - brzo_i2c_ACK_polling(1000); - return brzo_i2c_end_transaction(); + #if I2C_USE_BRZO + brzo_i2c_start_transaction(address, _i2c_scl_frequency); + brzo_i2c_ACK_polling(1000); + return brzo_i2c_end_transaction(); + #else + Wire.beginTransmission(address); + return Wire.endTransmission(); + #endif } -unsigned char i2cFind(size_t size, unsigned char * addresses) { +unsigned char i2cFindFirst(size_t size, unsigned char * addresses) { for (unsigned char i=0; i SENSOR_READ_INTERVAL) || (last_update == 0)) { + if (millis() - last_update > SENSOR_READ_INTERVAL) { last_update = millis(); report_count = (report_count + 1) % SENSOR_REPORT_EVERY; diff --git a/code/espurna/sensors/DallasSensor.h b/code/espurna/sensors/DallasSensor.h index 1ef1c2c1..c8d6fe55 100644 --- a/code/espurna/sensors/DallasSensor.h +++ b/code/espurna/sensors/DallasSensor.h @@ -114,7 +114,7 @@ class DallasSensor : public BaseSensor { // Descriptive name of the sensor String name() { char buffer[20]; - snprintf(buffer, sizeof(buffer), "OneWire @ GPIO%d", _gpio); + snprintf(buffer, sizeof(buffer), "Dallas @ GPIO%d", _gpio); return String(buffer); } diff --git a/code/espurna/sensors/SI7021Sensor.h b/code/espurna/sensors/SI7021Sensor.h new file mode 100644 index 00000000..d14a62d5 --- /dev/null +++ b/code/espurna/sensors/SI7021Sensor.h @@ -0,0 +1,159 @@ +// ----------------------------------------------------------------------------- +// DHT Sensor +// ----------------------------------------------------------------------------- + +#pragma once + +#include "Arduino.h" +#include "BaseSensor.h" +#if I2C_USE_BRZO +#include +#else +#include +#endif + +#define SI7021_SCL_FREQUENCY 200 + +#define SI7021_CHIP_SI7021 0x15 +#define SI7021_CHIP_HTU21D 0x32 + +#define SI7021_CMD_TMP_HOLD 0xE3 +#define SI7021_CMD_HUM_HOLD 0xE5 +#define SI7021_CMD_TMP_NOHOLD 0xF3 +#define SI7021_CMD_HUM_NOHOLD 0xF5 + +#define SI7021_ERROR_UNKNOW_CHIP -1 +#define SI7021_ERROR_TIMEOUT -2 +#define SI7021_ERROR_CRC -3 + +class SI7021Sensor : public BaseSensor { + + public: + + SI7021Sensor(unsigned char address = 0x40): BaseSensor() { + + // Asume I2C already started + _address = address; + + // Check device + #if I2C_USE_BRZO + uint8_t buffer[2] = {0xFC, 0xC9}; + brzo_i2c_start_transaction(_address, SI7021_SCL_FREQUENCY); + brzo_i2c_write(buffer, 2, false); + brzo_i2c_read(buffer, 1, false); + brzo_i2c_end_transaction(); + _chip = buffer[0]; + #else + Wire.beginTransmission(_address); + Wire.write(0xFC); + Wire.write(0xC9); + Wire.endTransmission(); + Wire.requestFrom(_address, (unsigned char) 1); + _chip = Wire.read(); + #endif + + if ((_chip != SI7021_CHIP_SI7021) & (_chip != SI7021_CHIP_HTU21D)) { + _error = SI7021_ERROR_UNKNOW_CHIP; + } else { + _count = 2; + } + + } + + // Descriptive name of the sensor + String name() { + char buffer[20]; + snprintf(buffer, sizeof(buffer), "%s @ I2C (0x%02X)", chipAsString().c_str(), _address); + return String(buffer); + } + + // Descriptive name of the slot # index + String slot(unsigned char index) { + return name(); + } + + // Type for slot # index + magnitude_t type(unsigned char index) { + if (index < _count) { + _error = SENSOR_ERROR_OK; + if (index == 0) return MAGNITUDE_TEMPERATURE; + if (index == 1) return MAGNITUDE_HUMIDITY; + } + _error = SENSOR_ERROR_OUT_OF_RANGE; + return MAGNITUDE_NONE; + } + + // Current value for slot # index + double value(unsigned char index) { + if (index < _count) { + _error = SENSOR_ERROR_OK; + double value; + if (index == 0) { + value = read(SI7021_CMD_TMP_NOHOLD); + value = (175.72 * value / 65536) - 46.85; + } + if (index == 1) { + value = read(SI7021_CMD_HUM_NOHOLD); + value = (125.0 * value / 65536) - 6; + value = constrain(value, 0, 100); + } + return value; + } + _error = SENSOR_ERROR_OUT_OF_RANGE; + return 0; + } + + protected: + + unsigned int read(uint8_t command) { + + unsigned char bytes = (command == 0xE0) ? 2 : 3; + + #if I2C_USE_BRZO + #else + Wire.beginTransmission(_address); + Wire.write(command); + Wire.endTransmission(); + #endif + + // When not using clock stretching (*_NOHOLD commands) delay here + // is needed to wait for the measurement. + // According to datasheet the max. conversion time is ~22ms + unsigned long start = millis(); + while (millis() - start < 50) delay(1); + + #if I2C_USE_BRZO + unsigned int msb = 0; + unsigned int lsb = 0; + #else + Wire.requestFrom(_address, bytes); + if (Wire.available() != bytes) return 100; + unsigned int msb = Wire.read(); + unsigned int lsb = Wire.read(); + #endif + + // Clear the last to bits of LSB to 00. + // According to datasheet LSB of RH is always xxxxxx10 + lsb &= 0xFC; + + unsigned int value = (msb << 8) | lsb; + + return value; + + } + + unsigned char chip() { + return _chip; + } + + String chipAsString() { + if (_chip == SI7021_CHIP_SI7021) return String("SI7021"); + if (_chip == SI7021_CHIP_HTU21D) return String("HTU21D"); + return String("Unknown"); + } + + unsigned char _address; + unsigned char _chip; + bool _found = false; + +};