@ -1,103 +0,0 @@ | |||||
/* | |||||
COUNTER MODULE | |||||
Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com> | |||||
*/ | |||||
#if COUNTER_SUPPORT | |||||
volatile unsigned long _counterCurrent = 0; | |||||
volatile unsigned long _counterLast = 0; | |||||
unsigned long _counterBuffer[COUNTER_REPORT_EVERY] = {0}; | |||||
unsigned char _counterBufferPointer = 0; | |||||
unsigned long _counterValue = 0; | |||||
// ----------------------------------------------------------------------------- | |||||
// COUNTER | |||||
// ----------------------------------------------------------------------------- | |||||
void ICACHE_RAM_ATTR _counterISR() { | |||||
if (millis() - _counterLast > COUNTER_DEBOUNCE) { | |||||
++_counterCurrent; | |||||
_counterLast = millis(); | |||||
} | |||||
} | |||||
void _counterWebSocketOnSend(JsonObject& root) { | |||||
root["counterVisible"] = 1; | |||||
root["counterValue"] = getCounter(); | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
unsigned long getCounter() { | |||||
return _counterValue; | |||||
} | |||||
void counterSetup() { | |||||
pinMode(COUNTER_PIN, COUNTER_PIN_MODE); | |||||
attachInterrupt(COUNTER_PIN, _counterISR, COUNTER_INTERRUPT_MODE); | |||||
#if WEB_SUPPORT | |||||
// Websockets | |||||
wsOnSendRegister(_counterWebSocketOnSend); | |||||
// API | |||||
apiRegister(COUNTER_TOPIC, COUNTER_TOPIC, [](char * buffer, size_t len) { | |||||
snprintf_P(buffer, len, PSTR("%d"), getCounter()); | |||||
}); | |||||
#endif | |||||
DEBUG_MSG_P(PSTR("[COUNTER] Counter on GPIO %d\n"), COUNTER_PIN); | |||||
} | |||||
void counterLoop() { | |||||
// Check if we should read new data | |||||
static unsigned long last_update = 0; | |||||
if ((millis() - last_update) < COUNTER_UPDATE_INTERVAL) return; | |||||
last_update = millis(); | |||||
// Update buffer counts | |||||
_counterValue = _counterValue - _counterBuffer[_counterBufferPointer] + _counterCurrent; | |||||
_counterBuffer[_counterBufferPointer] = _counterCurrent; | |||||
_counterCurrent = 0; | |||||
_counterBufferPointer = (_counterBufferPointer + 1) % COUNTER_REPORT_EVERY; | |||||
DEBUG_MSG_P(PSTR("[COUNTER] Value: %d\n"), _counterValue); | |||||
// Update websocket clients | |||||
#if WEB_SUPPORT | |||||
wsSend(_counterWebSocketOnSend); | |||||
#endif | |||||
// Do we have to report? | |||||
if (_counterBufferPointer == 0) { | |||||
// Send MQTT messages | |||||
#if MQTT_SUPPORT | |||||
mqttSend(getSetting("counterTopic", COUNTER_TOPIC).c_str(), String(_counterValue).c_str()); | |||||
#endif | |||||
// Send to Domoticz | |||||
#if DOMOTICZ_SUPPORT | |||||
domoticzSend("dczCountIdx", 0, String(_counterValue).c_str()); | |||||
#endif | |||||
// Send to InfluxDB | |||||
#if INFLUXDB_SUPPORT | |||||
idbSend(COUNTER_TOPIC, _counterValue); | |||||
#endif | |||||
} | |||||
} | |||||
#endif // COUNTER_SUPPORT |
@ -1,272 +0,0 @@ | |||||
/* | |||||
DHT MODULE | |||||
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com> | |||||
*/ | |||||
#if DHT_SUPPORT | |||||
double _dhtTemperature = 0; | |||||
unsigned int _dhtHumidity = 0; | |||||
bool _dhtIsConnected = false; | |||||
// ----------------------------------------------------------------------------- | |||||
// HAL | |||||
// https://github.com/gosouth/DHT22/blob/master/main/DHT22.c | |||||
// ----------------------------------------------------------------------------- | |||||
#define DHT_MAX_DATA 5 | |||||
#define DHT_MAX_ERRORS 5 | |||||
#define DHT_MIN_INTERVAL 2000 | |||||
#define DHT_OK 0 | |||||
#define DHT_CHECKSUM_ERROR -1 | |||||
#define DHT_TIMEOUT_ERROR -2 | |||||
#define DHT11 11 | |||||
#define DHT22 22 | |||||
#define DHT21 21 | |||||
#define AM2301 21 | |||||
unsigned long _getSignalLevel(unsigned char gpio, int usTimeOut, bool state) { | |||||
unsigned long uSec = 1; | |||||
while (digitalRead(gpio) == state) { | |||||
if (++uSec > usTimeOut) return 0; | |||||
delayMicroseconds(1); | |||||
} | |||||
return uSec; | |||||
} | |||||
int readDHT(unsigned char gpio, unsigned char type) { | |||||
static unsigned long last_ok = 0; | |||||
if ((last_ok > 0) && (millis() - last_ok < DHT_MIN_INTERVAL)) return DHT_OK; | |||||
unsigned long low = 0; | |||||
unsigned long high = 0; | |||||
static unsigned char errors = 0; | |||||
uint8_t dhtData[DHT_MAX_DATA] = {0}; | |||||
uint8_t byteInx = 0; | |||||
uint8_t bitInx = 7; | |||||
// Send start signal to DHT sensor | |||||
if (++errors > DHT_MAX_ERRORS) { | |||||
errors = 0; | |||||
digitalWrite(gpio, HIGH); | |||||
delay(250); | |||||
} | |||||
pinMode(gpio, OUTPUT); | |||||
noInterrupts(); | |||||
digitalWrite(gpio, LOW); | |||||
delayMicroseconds(500); | |||||
digitalWrite(gpio, HIGH); | |||||
delayMicroseconds(40); | |||||
pinMode(gpio, INPUT_PULLUP); | |||||
// No errors, read the 40 data bits | |||||
for( int k = 0; k < 41; k++ ) { | |||||
// Starts new data transmission with >50us low signal | |||||
low = _getSignalLevel(gpio, 56, LOW); | |||||
if (low==0) return DHT_TIMEOUT_ERROR; | |||||
// Check to see if after >70us rx data is a 0 or a 1 | |||||
high = _getSignalLevel(gpio, 75, HIGH); | |||||
if (high==0) return DHT_TIMEOUT_ERROR; | |||||
// Skip the first bit | |||||
if (k==0) continue; | |||||
// add the current read to the output data | |||||
// since all dhtData array where set to 0 at the start, | |||||
// only look for "1" (>28us us) | |||||
if (high > low) dhtData[byteInx] |= (1 << bitInx); | |||||
// index to next byte | |||||
if (bitInx == 0) { | |||||
bitInx = 7; | |||||
++byteInx; | |||||
} else { | |||||
--bitInx; | |||||
} | |||||
} | |||||
interrupts(); | |||||
// Verify checksum | |||||
if (dhtData[4] != ((dhtData[0] + dhtData[1] + dhtData[2] + dhtData[3]) & 0xFF)) { | |||||
return DHT_CHECKSUM_ERROR; | |||||
} | |||||
// Get humidity from Data[0] and Data[1] | |||||
if (type == DHT11) { | |||||
_dhtHumidity = dhtData[0]; | |||||
} else { | |||||
_dhtHumidity = dhtData[0] * 256 + dhtData[1]; | |||||
_dhtHumidity /= 10; | |||||
} | |||||
// Get temp from Data[2] and Data[3] | |||||
if (type == DHT11) { | |||||
_dhtTemperature = dhtData[2]; | |||||
} else { | |||||
_dhtTemperature = (dhtData[2] & 0x7F) * 256 + dhtData[3]; | |||||
_dhtTemperature /= 10; | |||||
if (dhtData[2] & 0x80) _dhtTemperature *= -1; | |||||
} | |||||
last_ok = millis(); | |||||
errors = 0; | |||||
return DHT_OK; | |||||
} | |||||
int readDHT() { | |||||
return readDHT(DHT_PIN, DHT_TYPE); | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// Private | |||||
// ----------------------------------------------------------------------------- | |||||
void _dhtWebSocketOnSend(JsonObject& root) { | |||||
root["dhtVisible"] = 1; | |||||
root["dhtConnected"] = getDHTIsConnected(); | |||||
if (getDHTIsConnected()) { | |||||
root["dhtTmp"] = getDHTTemperature(); | |||||
root["dhtHum"] = getDHTHumidity(); | |||||
} | |||||
root["tmpUnits"] = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt(); | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// Values | |||||
// ----------------------------------------------------------------------------- | |||||
bool getDHTIsConnected() { | |||||
return _dhtIsConnected; | |||||
} | |||||
double getDHTTemperature(bool celsius) { | |||||
double value = celsius ? _dhtTemperature : _dhtTemperature * 1.8 + 32; | |||||
double correction = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION).toFloat(); | |||||
return roundTo(value + correction, TEMPERATURE_DECIMALS); | |||||
} | |||||
double getDHTTemperature() { | |||||
bool celsius = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt() == TMP_CELSIUS; | |||||
return getDHTTemperature(celsius); | |||||
} | |||||
unsigned int getDHTHumidity() { | |||||
return _dhtHumidity; | |||||
} | |||||
void dhtSetup() { | |||||
#if DHT_PULLUP | |||||
pinMode(DHT_PIN, INPUT_PULLUP); | |||||
#endif | |||||
#if WEB_SUPPORT | |||||
// Websockets | |||||
wsOnSendRegister(_dhtWebSocketOnSend); | |||||
apiRegister(DHT_TEMPERATURE_TOPIC, DHT_TEMPERATURE_TOPIC, [](char * buffer, size_t len) { | |||||
dtostrf(getDHTTemperature(), 1-len, 1, buffer); | |||||
}); | |||||
apiRegister(DHT_HUMIDITY_TOPIC, DHT_HUMIDITY_TOPIC, [](char * buffer, size_t len) { | |||||
snprintf_P(buffer, len, PSTR("%d"), getDHTHumidity()); | |||||
}); | |||||
#endif | |||||
} | |||||
void dhtLoop() { | |||||
static unsigned long last_update = 0; | |||||
static double last_temperature = 0.0; | |||||
static unsigned int last_humidity = 0; | |||||
// Check if we should read new data | |||||
if ((millis() - last_update > DHT_UPDATE_INTERVAL) || (last_update == 0)) { | |||||
last_update = millis(); | |||||
// Read sensor data | |||||
int response = readDHT(DHT_PIN, DHT_TYPE); | |||||
if (response != DHT_OK) { | |||||
DEBUG_MSG_P(PSTR("[DHT] Error: %d\n"), response); | |||||
return; | |||||
} | |||||
_dhtIsConnected = true; | |||||
// Get values | |||||
bool celsius = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS).toInt() == TMP_CELSIUS; | |||||
double t = getDHTTemperature(celsius); | |||||
unsigned int h = getDHTHumidity(); | |||||
// Build strings | |||||
char temperature[6]; | |||||
char humidity[6]; | |||||
dtostrf(t, 1-sizeof(temperature), 1, temperature); | |||||
itoa((unsigned int) h, humidity, 10); | |||||
// Debug | |||||
DEBUG_MSG_P(PSTR("[DHT] Temperature: %s%s\n"), temperature, celsius ? "ºC" : "ºF"); | |||||
DEBUG_MSG_P(PSTR("[DHT] Humidity: %s\n"), humidity); | |||||
// If the new temperature & humidity are different from the last | |||||
if ((fabs(t - last_temperature) >= TEMPERATURE_MIN_CHANGE) | |||||
|| (abs(h - last_humidity) >= HUMIDITY_MIN_CHANGE)) { | |||||
last_temperature = t; | |||||
last_humidity = h; | |||||
// Send MQTT messages | |||||
#if MQTT_SUPPORT | |||||
mqttSend(getSetting("dhtTmpTopic", DHT_TEMPERATURE_TOPIC).c_str(), temperature); | |||||
mqttSend(getSetting("dhtHumTopic", DHT_HUMIDITY_TOPIC).c_str(), humidity); | |||||
#endif | |||||
// Send to Domoticz | |||||
#if DOMOTICZ_SUPPORT | |||||
{ | |||||
domoticzSend("dczTmpIdx", 0, temperature); | |||||
int status; | |||||
if (h > 70) { | |||||
status = HUMIDITY_WET; | |||||
} else if (h > 45) { | |||||
status = HUMIDITY_COMFORTABLE; | |||||
} else if (h > 30) { | |||||
status = HUMIDITY_NORMAL; | |||||
} else { | |||||
status = HUMIDITY_DRY; | |||||
} | |||||
char buffer[2]; | |||||
snprintf_P(buffer, sizeof(buffer), PSTR("%d"), status); | |||||
domoticzSend("dczHumIdx", humidity, buffer); | |||||
} | |||||
#endif | |||||
#if INFLUXDB_SUPPORT | |||||
idbSend(getSetting("dhtTmpTopic", DHT_TEMPERATURE_TOPIC).c_str(), temperature); | |||||
idbSend(getSetting("dhtHumTopic", DHT_HUMIDITY_TOPIC).c_str(), humidity); | |||||
#endif | |||||
} | |||||
// Update websocket clients | |||||
#if WEB_SUPPORT | |||||
wsSend(_dhtWebSocketOnSend); | |||||
#endif | |||||
} | |||||
} | |||||
#endif |
@ -1,147 +0,0 @@ | |||||
/* | |||||
DS18B20 MODULE | |||||
Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com> | |||||
*/ | |||||
#if DS18B20_SUPPORT | |||||
#include <OneWire.h> | |||||
#include <DallasTemperature.h> | |||||
OneWire oneWire(DS18B20_PIN); | |||||
DallasTemperature ds18b20(&oneWire); | |||||
bool _dsIsConnected = false; | |||||
double _dsTemperature = 0; | |||||
// ----------------------------------------------------------------------------- | |||||
// Private | |||||
// ----------------------------------------------------------------------------- | |||||
void _dsWebSocketOnSend(JsonObject& root) { | |||||
root["dsVisible"] = 1; | |||||
root["dsConnected"] = getDSIsConnected(); | |||||
if (getDSIsConnected()) { | |||||
root["dsTmp"] = getDSTemperature(); | |||||
} | |||||
root["tmpUnits"] = getSetting("tmpUnits", TMP_UNITS).toInt(); | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// DS18B20 | |||||
// ----------------------------------------------------------------------------- | |||||
bool getDSIsConnected() { | |||||
return _dsIsConnected; | |||||
} | |||||
double getDSTemperature(bool celsius) { | |||||
double value = celsius ? _dsTemperature : _dsTemperature * 1.8 + 32; | |||||
double correction = getSetting("tmpCorrection", TEMPERATURE_CORRECTION).toFloat(); | |||||
return roundTo(value + correction, TEMPERATURE_DECIMALS); | |||||
} | |||||
double getDSTemperature() { | |||||
bool celsius = getSetting("tmpUnits", TMP_UNITS).toInt() == TMP_CELSIUS; | |||||
return getDSTemperature(celsius); | |||||
} | |||||
void dsSetup() { | |||||
#if DS18B20_PULLUP | |||||
pinMode(DS18B20_PIN, INPUT_PULLUP); | |||||
#endif | |||||
ds18b20.begin(); | |||||
ds18b20.setWaitForConversion(false); | |||||
#if WEB_SUPPORT | |||||
wsOnSendRegister(_dsWebSocketOnSend); | |||||
apiRegister(DS18B20_TEMPERATURE_TOPIC, DS18B20_TEMPERATURE_TOPIC, [](char * buffer, size_t len) { | |||||
dtostrf(getDSTemperature(), 1-len, 1, buffer); | |||||
}); | |||||
#endif | |||||
} | |||||
void dsLoop() { | |||||
static unsigned long last_update = 0; | |||||
static double last_temperature = 0.0; | |||||
static bool requested = false; | |||||
if ((millis() - last_update > DS18B20_UPDATE_INTERVAL) || (last_update == 0)) { | |||||
if (!requested) { | |||||
ds18b20.requestTemperatures(); | |||||
requested = true; | |||||
// Requesting takes time, so data will probably not be available in this round | |||||
return; | |||||
} | |||||
// Check if requested data is already available | |||||
if (!ds18b20.isConversionComplete()) return; | |||||
requested = false; | |||||
last_update = millis(); | |||||
// Read sensor data | |||||
double t = ds18b20.getTempCByIndex(0); | |||||
// Check returned value | |||||
if (t == DEVICE_DISCONNECTED_C) { | |||||
_dsIsConnected = false; | |||||
DEBUG_MSG_P(PSTR("[DS18B20] Not connected\n")); | |||||
return; | |||||
} else { | |||||
_dsIsConnected = true; | |||||
} | |||||
// Save & convert | |||||
_dsTemperature = t; | |||||
bool celsius = getSetting("tmpUnits", TMP_UNITS).toInt() == TMP_CELSIUS; | |||||
t = getDSTemperature(celsius); | |||||
// Build string | |||||
char temperature[6]; | |||||
dtostrf(getDSTemperature(celsius), 1-sizeof(temperature), 1, temperature); | |||||
// Debug | |||||
DEBUG_MSG_P(PSTR("[DS18B20] Temperature: %s%s\n"), temperature, celsius ? "ºC" : "ºF"); | |||||
// If the new temperature is different from the last | |||||
if (fabs(_dsTemperature - last_temperature) >= TEMPERATURE_MIN_CHANGE) { | |||||
last_temperature = _dsTemperature; | |||||
// Send MQTT messages | |||||
#if MQTT_SUPPORT | |||||
mqttSend(getSetting("dsTmpTopic", DS18B20_TEMPERATURE_TOPIC).c_str(), temperature); | |||||
#endif | |||||
// Send to Domoticz | |||||
#if DOMOTICZ_SUPPORT | |||||
domoticzSend("dczTmpIdx", 0, temperature); | |||||
#endif | |||||
#if INFLUXDB_SUPPORT | |||||
idbSend(getSetting("dsTmpTopic", DS18B20_TEMPERATURE_TOPIC).c_str(), temperature); | |||||
#endif | |||||
} | |||||
// Update websocket clients | |||||
#if WEB_SUPPORT | |||||
wsSend(_dsWebSocketOnSend); | |||||
#endif | |||||
} | |||||
} | |||||
#endif |
@ -0,0 +1,105 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// DHT Sensor | |||||
// ----------------------------------------------------------------------------- | |||||
#pragma once | |||||
#include "Arduino.h" | |||||
#include "BaseSensor.h" | |||||
#include <OneWire.h> | |||||
#include <DallasTemperature.h> | |||||
#define DS18B20_OK 0 | |||||
#define DS18B20_NOT_FOUND 1 | |||||
#define DS18B20_OUT_OF_RANGE 2 | |||||
#define DS18B20_CONVERSION_ERROR 3 | |||||
class DS18B20Sensor : public BaseSensor { | |||||
public: | |||||
DS18B20Sensor(unsigned char gpio, bool pull_up = false): BaseSensor() { | |||||
_gpio = gpio; | |||||
if (pull_up) pinMode(_gpio, INPUT_PULLUP); | |||||
init(); | |||||
} | |||||
// Pre-read hook (usually to populate registers with up-to-date data) | |||||
void pre() { | |||||
_device->requestTemperatures(); | |||||
// TODO: enable? | |||||
/* | |||||
while (!_device->isConversionComplete()) { | |||||
delay(1); | |||||
} | |||||
*/ | |||||
} | |||||
// Descriptive name of the sensor | |||||
String name() { | |||||
char buffer[20]; | |||||
snprintf(buffer, sizeof(buffer), "DS18B20 %s@ GPIO%d", | |||||
_device->isParasitePowerMode() ? "(P) " : "", | |||||
_gpio | |||||
); | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
if (index < _count) { | |||||
DeviceAddress address; | |||||
_device->getAddress(address, index); | |||||
char buffer[40]; | |||||
snprintf(buffer, sizeof(buffer), "%02X%02X%02X%02X%02X%02X%02X%02X @ %s", | |||||
address[0], address[1], address[2], address[3], | |||||
address[4], address[5], address[6], address[7], | |||||
name().c_str() | |||||
); | |||||
return String(buffer); | |||||
} | |||||
_error = DS18B20_OUT_OF_RANGE; | |||||
return String(); | |||||
} | |||||
// Type for slot # index | |||||
magnitude_t type(unsigned char index) { | |||||
if (index < _count) return MAGNITUDE_TEMPERATURE; | |||||
_error = DS18B20_OUT_OF_RANGE; | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
if (index < _count) { | |||||
double t = _device->getTempCByIndex(index); | |||||
if (t != DEVICE_DISCONNECTED_C) { | |||||
_error = DS18B20_OK; | |||||
return t; | |||||
} | |||||
_error = DS18B20_CONVERSION_ERROR; | |||||
} | |||||
_error = DS18B20_OUT_OF_RANGE; | |||||
return 0; | |||||
} | |||||
protected: | |||||
void init() { | |||||
OneWire * wire = new OneWire(_gpio); | |||||
_device = new DallasTemperature(wire); | |||||
_device->begin(); | |||||
_device->setWaitForConversion(false); | |||||
_count = _device->getDeviceCount(); | |||||
if (_count == 0) _error = DS18B20_NOT_FOUND; | |||||
} | |||||
unsigned char _gpio; | |||||
DallasTemperature * _device; | |||||
}; |
@ -0,0 +1,64 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// DHT Sensor | |||||
// ----------------------------------------------------------------------------- | |||||
#pragma once | |||||
#include "Arduino.h" | |||||
#include "BaseSensor.h" | |||||
class EventSensor : public BaseSensor { | |||||
public: | |||||
void InterruptHandler() { | |||||
static unsigned long last = 0; | |||||
if (millis() - last > _debounce) { | |||||
_events = _events + 1; | |||||
last = millis(); | |||||
} | |||||
} | |||||
EventSensor(unsigned char gpio, int pin_mode, unsigned long debounce): BaseSensor() { | |||||
_gpio = gpio; | |||||
_count = 1; | |||||
_debounce = debounce; | |||||
pinMode(_gpio, pin_mode); | |||||
} | |||||
// Descriptive name of the sensor | |||||
String name() { | |||||
char buffer[20]; | |||||
snprintf(buffer, sizeof(buffer), "EVENT @ GPIO%d", _gpio); | |||||
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 == 0) return MAGNITUDE_EVENTS; | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
double value = 0; | |||||
if (index == 0) { | |||||
value = _events; | |||||
_events = 0; | |||||
}; | |||||
return value; | |||||
} | |||||
protected: | |||||
volatile unsigned long _events = 0; | |||||
unsigned long _debounce = 0; | |||||
unsigned char _gpio; | |||||
}; |