Added temperature and relay nodes

This commit is contained in:
Ard Kuijpers
2020-03-08 20:10:23 +01:00
parent 82c7029e57
commit aa9fa938c4
12 changed files with 731 additions and 101 deletions

View File

@@ -0,0 +1,105 @@
/*
* DHT22Node.cpp
* Homie Node for DHT22 sensors using Adafruit DHT22 library.
*
* Version: 1.0
* Author: Lübbe Onken (http://github.com/luebbe)
*/
#include "DHT22Node.hpp"
#define DHTTYPE DHT22
DHT22Node::DHT22Node(const char *name, const int sensorPin, const int measurementInterval)
: SensorNode(name, "DHT22"),
_sensorPin(sensorPin),
_measurementInterval(measurementInterval),
_lastMeasurement(0)
{
if (_sensorPin > DEFAULTPIN)
{
dht = new DHT(_sensorPin, DHTTYPE);
}
advertise(cStatusTopic)
.setDatatype("enum")
.setFormat("error, ok");
advertise(cTemperatureTopic)
.setDatatype("float")
.setFormat("-40:125")
.setUnit(cUnitDegrees);
advertise(cHumidityTopic)
.setDatatype("float")
.setFormat("0:100")
.setUnit(cUnitPercent);
advertise(cAbsHumidityTopic)
.setDatatype("float")
.setUnit(cUnitMgm3);
}
void DHT22Node::printCaption()
{
Homie.getLogger() << cCaption << " pin[" << _sensorPin << "]:" << endl;
}
void DHT22Node::send()
{
printCaption();
if (isnan(temperature) || isnan(humidity))
{
Homie.getLogger() << cIndent << "Error reading from Sensor" << endl;
if (Homie.isConnected())
{
setProperty(cStatusTopic).send("error");
}
}
else
{
float absHumidity = computeAbsoluteHumidity(temperature, humidity);
Homie.getLogger() << cIndent << "Temperature: " << temperature << " °C" << endl;
Homie.getLogger() << cIndent << "Humidity: " << humidity << " %" << endl;
Homie.getLogger() << cIndent << "Abs humidity: " << absHumidity << " g/m³" << endl;
if (Homie.isConnected())
{
setProperty(cStatusTopic).send("ok");
setProperty(cTemperatureTopic).send(String(temperature));
setProperty(cHumidityTopic).send(String(humidity));
setProperty(cAbsHumidityTopic).send(String(absHumidity));
}
}
}
void DHT22Node::loop()
{
if (dht)
{
if ((millis() - _lastMeasurement >= _measurementInterval * 1000UL) ||
(_lastMeasurement == 0))
{
temperature = dht->readTemperature();
humidity = dht->readHumidity();
fixRange(&temperature, cMinTemp, cMaxTemp);
fixRange(&humidity, cMinHumid, cMaxHumid);
send();
_lastMeasurement = millis();
}
}
}
void DHT22Node::setup()
{
printCaption();
Homie.getLogger() << cIndent << "Reading interval: " << _measurementInterval << " s" << endl;
if (dht)
{
dht->begin();
}
}

View File

@@ -0,0 +1,50 @@
/*
* DHT22Node.hpp
* Homie Node for DHT-22 sensors using Adafruit DHT library.
*
* Version: 1.0
* Author: Lübbe Onken (http://github.com/luebbe)
*/
#pragma once
#include <Adafruit_Sensor.h>
#include <DHT_U.h>
#include "SensorNode.hpp"
#include "constants.hpp"
#define DEFAULTPIN -1
class DHT22Node : public SensorNode
{
private:
const float cMinTemp = -40.0;
const float cMaxTemp = 125.0;
const char *cCaption = "• DHT22 sensor";
const char *cIndent = "";
int _sensorPin;
unsigned long _measurementInterval;
unsigned long _lastMeasurement;
float temperature = NAN;
float humidity = NAN;
DHT *dht = NULL;
void printCaption();
void send();
protected:
virtual void setup() override;
virtual void loop() override;
public:
explicit DHT22Node(const char *name,
const int sensorPin = DEFAULTPIN,
const int measurementInterval = MEASUREMENT_INTERVAL);
float getHumidity() const { return humidity; }
float getTemperature() const { return temperature; }
};

147
lib/HomieNodes/PingNode.cpp Normal file
View File

@@ -0,0 +1,147 @@
/*
* BME280Node.cpp
* Homie Node for BME280 sensors using Adafruit BME280 library.
*
* Version: 1.1
* Author: Lübbe Onken (http://github.com/luebbe)
* Author: Markus Haack (http://github.com/mhaack)
*/
#include "PingNode.hpp"
#include <Homie.h>
bool checkBounds(float value, float min, float max) {
return !isnan(value) && value >= min && value <= max;
}
PingNode::PingNode(const char *name, const int triggerPin, const int echoPin,
const int measurementInterval, const int publishInterval)
: SensorNode(name, "RCW-0001"),
_triggerPin(triggerPin), _echoPin(echoPin),_lastMeasurement(0), _lastPublish(0)
{
_measurementInterval = (measurementInterval > MIN_INTERVAL) ? measurementInterval : MIN_INTERVAL;
_publishInterval = (publishInterval > int(_measurementInterval)) ? publishInterval : _measurementInterval;
setMicrosecondsToMetersFactor(20);
if (_triggerPin > DEFAULTPIN && _echoPin > DEFAULTPIN) {
sonar = new NewPing(_triggerPin,_echoPin,cMaxDistance*100.0);
}
advertise(cDistanceTopic)
.setDatatype("float")
.setFormat("0:3")
.setUnit(cUnitMeter);
advertise(cPingTopic)
.setDatatype("float")
.setUnit(cUnitMicrosecond);
advertise(cStatusTopic)
.setDatatype("enum")
.setFormat("error, ok");
advertise(cChangedTopic)
.setName("Obstacle changed")
.setDatatype("boolean");
}
void PingNode::printCaption()
{
Homie.getLogger() << cCaption << " triggerpin[" << _triggerPin << "], echopin[" << _echoPin << "]:" << endl;
}
void PingNode::send()
{
printCaption();
Homie.getLogger() << cIndent << "Ping: " << _ping_us << " " << cUnitMicrosecond << endl;
Homie.getLogger() << cIndent << "Distance: " << _distance << " " << cUnitMeter << endl;
bool valid = _distance > 0;
Homie.getLogger() << cIndent << "Status: " << (valid ? "ok" : "error") << endl;
bool changed = signalChange(_distance, _lastDistance);
Homie.getLogger() << cIndent << "Changed: " << (changed ? "true" : "false") << " " << endl;
if (Homie.isConnected())
{
setProperty(cStatusTopic).send(valid ? "ok" : "error");
if (valid) {
setProperty(cDistanceTopic).send(String(_distance));
setProperty(cChangedTopic).send(changed ? "true": "false");
}
}
if (changed)
{
_changeHandler();
}
if (valid) {
_lastDistance = _distance;
_distance = -1;
}
}
void PingNode::loop()
{
if (sonar) {
if (millis() - _lastMeasurement >= _measurementInterval * 1000UL || _lastMeasurement == 0)
{
float ping_us = sonar->ping_median();
// Calculating the distance @ 10 °C from d = t_ping /2 * c => t_ping /2 * 337 [m/s] => t_ping_us / 1e-6 * 1/2 * 337
float newDistance = ping_us*0.0001685;
fixRange(&newDistance, cMinDistance, cMaxDistance);
if (newDistance > 0) {
_ping_us = ping_us;
_distance = newDistance;
}
_lastMeasurement = millis();
}
if (millis() - _lastPublish >= _publishInterval * 1000UL || _lastPublish == 0)
if (_distance > 0) {
send();
_lastPublish = millis();
_distance = 0;
}
}
}
void PingNode::onReadyToOperate()
{
if (Homie.isConnected())
{
setProperty(cStatusTopic).send("ok");
}
};
void PingNode::setup()
{
printCaption();
Homie.getLogger() << cIndent << "Reading interval: " << _measurementInterval << " s" << endl;
Homie.getLogger() << cIndent << "Publish interval: " << _publishInterval << " s" << endl;
}
void PingNode::setMicrosecondsToMetersFactor(float temperatureCelcius)
{
//float soundSpeed = 337.0; // @ 10°C
float soundSpeed = 331.4 * 0.6*temperatureCelcius;
// Calculating the distance from d = t_ping /2 * c => t_ping /2 * 337 [m/s] => t_ping_us / 1e-6 * 1/2 * 337
_microseconds2meter = 0.5e-6 * soundSpeed;
}
float PingNode::getRawEchoTime() {
// Clears the trigPin
digitalWrite(_triggerPin, LOW);
delayMicroseconds(2);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(_triggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(_triggerPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
return pulseIn(_echoPin, HIGH);
}
bool PingNode::signalChange(float distance, float lastDistance) {
return fabs(distance - lastDistance) > cMinimumChange;
}
PingNode& PingNode::setChangeHandler(const ChangeHandler& changeHandler) {
_changeHandler = changeHandler;
return *this;
}

View File

@@ -0,0 +1,71 @@
/*
* BME280Node.h
* Homie Node for BME280 sensors using Adafruit BME280 library.
*
* Version: 1.1
* Author: Lübbe Onken (http://github.com/luebbe)
* Author: Markus Haack (http://github.com/mhaack)
*/
#pragma once
#include "NewPing.h"
#include "SensorNode.hpp"
#include "constants.hpp"
#define DEFAULTPIN -1
class PingNode : public SensorNode
{
public:
typedef std::function<void()> ChangeHandler;
private:
const float cMinDistance = 0.0;
const float cMaxDistance = 3.0;
const float cMinimumChange = 0.2;
static const int MIN_INTERVAL = 1; // in seconds
const char *cCaption = "• RCW-0001 sensor";
const char *cIndent = "";
int _triggerPin;
int _echoPin;
float _microseconds2meter;
unsigned long _measurementInterval;
unsigned long _lastMeasurement;
unsigned long _publishInterval;
unsigned long _lastPublish;
NewPing* sonar;
float _distance = NAN;
int _ping_us = NAN;
float _lastDistance = 0;
ChangeHandler _changeHandler = [](){};
float getRawEchoTime();
void setMicrosecondsToMetersFactor(float temperatureCelcius);
bool signalChange(float distance, float lastDistance);
void printCaption();
void send();
protected:
virtual void setup() override;
virtual void loop() override;
virtual void onReadyToOperate() override;
static const int DEFAULT_MEASUREMENT_INTERVAL = 1;
static const int DEFAULT_PUBLISH_INTERVAL = 5;
public:
explicit PingNode(const char *name,
const int triggerPin = DEFAULTPIN,
const int echoPin = DEFAULTPIN,
const int measurementInterval = DEFAULT_MEASUREMENT_INTERVAL,
const int publishInterval = DEFAULT_PUBLISH_INTERVAL);
float getDistance() const { return _distance; }
void setTemperature(float temperatureCelcius) { setMicrosecondsToMetersFactor(temperatureCelcius); }
PingNode& setChangeHandler(const ChangeHandler& changeHandler);
};

View File

@@ -0,0 +1,179 @@
/*
* RelayNode.cpp
* Homie Node for a Relay with optional status indicator LED
*
* Version: 1.1
* Author: Lübbe Onken (http://github.com/luebbe)
*/
#include "RelayNode.hpp"
RelayNode::RelayNode(const char *name, const int relayPin, const int ledPin, const bool reverseSignal)
: HomieNode(name, "RelayNode", "actor")
{
_relayPin = relayPin;
_ledPin = ledPin;
if (reverseSignal)
{
_relayOnValue = LOW;
_relayOffValue = HIGH;
}
else
{
_relayOnValue = HIGH;
_relayOffValue = LOW;
}
advertise("on")
.setDatatype("boolean")
.settable();
advertise("timeout")
.setDatatype("integer")
.settable();
}
HomieInternals::Uptime relayUptime;
bool RelayNode::handleOnOff(const String &value)
{
if (value == "true" || value == "false")
{
setRelay(value == "true");
return true;
}
else
{
return false;
}
}
#define IS_INTEGER(s) (s == String(s.toInt()))
bool RelayNode::handleTimeout(const String &value)
{
if (IS_INTEGER(value))
{
long timeout = value.toInt();
if (timeout > 0)
{
setRelay(true, timeout);
return true;
}
}
return false;
}
bool RelayNode::handleInput(const HomieRange &range, const String &property, const String &value)
{
Homie.getLogger() << "Message: " << property << " " << value << endl;
if (property.equals("on"))
{
return handleOnOff(value);
}
else if (property.equals("timeout"))
{
return handleTimeout(value);
}
else
{
return false;
}
}
void RelayNode::printCaption()
{
Homie.getLogger() << cCaption << endl;
}
void RelayNode::setLed(bool on)
{
if (_ledPin > DEFAULTPIN)
{
digitalWrite(_ledPin, on ? LOW : HIGH); // LOW = LED on
}
}
void RelayNode::setRelay(bool on, long timeoutSecs)
{
printCaption();
if (_relayPin > DEFAULTPIN)
{
setRelayState(on);
if (on && timeoutSecs > 0)
{
_timeout = relayUptime.getSeconds() + timeoutSecs;
}
else
{
_timeout = 0;
}
sendState();
}
else
{
Homie.getLogger() << cIndent << "No Relay Pin!" << endl;
}
setLed(on);
}
void RelayNode::sendState()
{
bool on = getRelayState();
Homie.getLogger() << cIndent << "Relay is " << (on ? "on" : "off") << endl;
if (Homie.isConnected())
{
setProperty("on").send(on ? "true" : "false");
setProperty("timeout").send(String(long(_timeout)));
}
}
void RelayNode::setRelayState(bool on)
{
digitalWrite(_relayPin, on ? _relayOnValue : _relayOffValue);
}
bool RelayNode::getRelayState()
{
return digitalRead(_relayPin) == _relayOnValue;
}
void RelayNode::toggleRelay()
{
setRelay(!getRelayState());
}
void RelayNode::setupRelay()
{
pinMode(_relayPin, OUTPUT);
}
void RelayNode::setup()
{
printCaption();
Homie.getLogger() << cIndent << "Relay Pin: " << _relayPin << endl
<< cIndent << "Led Pin : " << _ledPin << endl;
if (_ledPin > DEFAULTPIN)
{
pinMode(_ledPin, OUTPUT);
setLed(false);
}
if (_relayPin > DEFAULTPIN)
{
setupRelay();
setRelay(false);
}
}
void RelayNode::loop()
{
relayUptime.update();
if ((_timeout > 0) && getRelayState() && (_timeout < relayUptime.getSeconds()))
{
setRelay(false);
}
}

View File

@@ -0,0 +1,49 @@
/*
* RelayNode.hpp
* Homie Node for a Relay with optional status indicator LED
*
* Version: 1.0
* Author: Lübbe Onken (http://github.com/luebbe)
*/
#pragma once
#include <Homie.hpp>
#define DEFAULTPIN -1
class RelayNode : public HomieNode
{
private:
const char *cCaption = "• Relay:";
const char *cIndent = "";
int _relayPin;
int _ledPin;
uint8_t _relayOnValue;
uint8_t _relayOffValue;
uint64_t _timeout;
bool handleOnOff(const String &value);
bool handleTimeout(const String &value);
void printCaption();
void sendState();
void setupRelay();
bool getRelayState();
void setRelayState(bool on);
void setLed(bool on);
protected:
virtual bool handleInput(const HomieRange &range, const String &property, const String &value) override;
virtual void setup() override;
virtual void loop() override;
public:
explicit RelayNode(const char *name, const int relayPin = DEFAULTPIN, const int ledPin = DEFAULTPIN, const bool reverseSignal = false);
void setRelay(bool on, long timeoutSecs = 0);
void toggleRelay();
};

View File

@@ -0,0 +1,48 @@
/*
* SensorNode.hpp
* Homie Node for genric sensors.
* Provides a limit method for measurement values
*
* Version: 1.0
* Author: Lübbe Onken (http://github.com/luebbe)
*/
#include "SensorNode.hpp"
SensorNode::SensorNode(const char *name, const char *type)
: HomieNode(name, type, "sensor")
{
}
float SensorNode::computeAbsoluteHumidity(float temperature, float percentHumidity) {
// Calculate the absolute humidity in g/m³
// https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
float absHumidity;
float absTemperature;
absTemperature = temperature + 273.15;
absHumidity = 6.112;
absHumidity *= exp((17.67 * temperature) / (243.5 + temperature));
absHumidity *= percentHumidity;
absHumidity *= 2.1674;
absHumidity /= absTemperature;
return absHumidity;
}
void SensorNode::fixRange(float *value, float min, float max)
{
if (isnan(*value))
{
return;
}
else if (*value < min)
{
*value = min;
}
else if (*value > max)
{
*value = max;
};
}

View File

@@ -0,0 +1,26 @@
/*
* SensorNode.hpp
* Homie Node for genric sensors.
* Provides a limit method for measurement values
*
* Version: 1.0
* Author: Lübbe Onken (http://github.com/luebbe)
*/
#pragma once
#include <Homie.hpp>
class SensorNode : public HomieNode
{
protected:
const float cMinHumid = 0.0;
const float cMaxHumid = 100.0;
static const int MEASUREMENT_INTERVAL = 300;
float computeAbsoluteHumidity(float temperature, float percentHumidity);
void fixRange(float *value, float min, float max);
public:
explicit SensorNode(const char *name, const char *type);
};

View File

@@ -0,0 +1,25 @@
// Units
#define cUnitDegrees "°C"
#define cUnitHpa "hPa"
#define cUnitMgm3 "g/m³"
#define cUnitPercent "%"
#define cUnitVolt "V"
#define cUnitMeter "m"
#define cUnitMicrosecond "μs"
// Topics
#define cStatusTopic "status"
#define cUnitTopic "unit"
#define cTemperatureTopic "temperature"
#define cTemperatureUnitTopic cTemperatureTopic "/" cUnitTopic
#define cHumidityTopic "humidity"
#define cPressureTopic "pressure"
#define cAbsHumidityTopic "abshumidity"
#define cVoltageTopic "voltage"
#define cBatteryLevelTopic "batterylevel"
#define cDistanceTopic "distance"
#define cPingTopic "ping"
#define cChangedTopic "changed"
#define cValidTopic "valid"