@ -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; | |||
}; |