Added temperature and relay nodes
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
data/homie/config.json
|
||||
|
||||
105
lib/HomieNodes/DHT22Node.cpp
Normal file
105
lib/HomieNodes/DHT22Node.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
50
lib/HomieNodes/DHT22Node.hpp
Normal file
50
lib/HomieNodes/DHT22Node.hpp
Normal 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
147
lib/HomieNodes/PingNode.cpp
Normal 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;
|
||||
}
|
||||
71
lib/HomieNodes/PingNode.hpp
Normal file
71
lib/HomieNodes/PingNode.hpp
Normal 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);
|
||||
};
|
||||
179
lib/HomieNodes/RelayNode.cpp
Normal file
179
lib/HomieNodes/RelayNode.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
49
lib/HomieNodes/RelayNode.hpp
Normal file
49
lib/HomieNodes/RelayNode.hpp
Normal 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();
|
||||
};
|
||||
48
lib/HomieNodes/SensorNode.cpp
Normal file
48
lib/HomieNodes/SensorNode.cpp
Normal 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;
|
||||
};
|
||||
}
|
||||
26
lib/HomieNodes/SensorNode.hpp
Normal file
26
lib/HomieNodes/SensorNode.hpp
Normal 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);
|
||||
};
|
||||
25
lib/HomieNodes/constants.hpp
Normal file
25
lib/HomieNodes/constants.hpp
Normal 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"
|
||||
|
||||
|
||||
@@ -17,4 +17,5 @@ monitor_speed = 115200
|
||||
lib_deps =
|
||||
NewPing
|
||||
Homie
|
||||
|
||||
DHT sensor library
|
||||
Adafruit Unified Sensor
|
||||
|
||||
128
src/main.cpp
128
src/main.cpp
@@ -1,28 +1,38 @@
|
||||
#include <NewPing.h>
|
||||
#include <Homie.h>
|
||||
|
||||
#include "PingNode.hpp"
|
||||
#include "RelayNode.hpp"
|
||||
#include "DHT22Node.hpp"
|
||||
|
||||
#define TURN_ON LOW
|
||||
#define TURN_OFF HIGH
|
||||
|
||||
const int trigPin = D1; //D1
|
||||
const int echoPin = D2; //D2
|
||||
const int trigPin = D1;
|
||||
const int echoPin = D2;
|
||||
const int relayPin = 14; // D5
|
||||
const int dhtPin = D7; ;
|
||||
const int ledPin = LED_BUILTIN;
|
||||
const float minDistance = 0.01;
|
||||
const float maxDistance = 4.0;
|
||||
const float minimumChange = 0.2;
|
||||
|
||||
#define MEASURE_INTERVAL 1000 // ms
|
||||
#define PUBLISH_INTERVAL 5000 // ms
|
||||
unsigned long HEARTBEAT_INTERVAL = 5;
|
||||
unsigned long lastHeartbeat = 0;
|
||||
|
||||
NewPing sonar(trigPin,echoPin,maxDistance*100.0);
|
||||
HomieNode obstacleNode("obstacle", "Obstacle", "object");
|
||||
PingNode obstacleNode("obstacle",trigPin,echoPin);
|
||||
DHT22Node airNode("air",dhtPin,20);
|
||||
RelayNode relayNode("relay",relayPin,ledPin);
|
||||
|
||||
void signal_led(bool ons = true);
|
||||
void signal_led(bool on = true);
|
||||
void heartbeat_led(int times = 2);
|
||||
void longbeat_led();
|
||||
void loopHandler();
|
||||
void setupHandler();
|
||||
float readDistance();
|
||||
|
||||
void changeHandler() {
|
||||
signal_led();
|
||||
}
|
||||
|
||||
void loopHandler() {
|
||||
if (millis() - lastHeartbeat > HEARTBEAT_INTERVAL * 1000UL || lastHeartbeat == 0) {
|
||||
heartbeat_led();
|
||||
lastHeartbeat = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
@@ -31,14 +41,9 @@ void setup() {
|
||||
pinMode(ledPin, OUTPUT);
|
||||
|
||||
Homie_setBrand("EtxeanIoT");
|
||||
Homie_setFirmware("etxean-distancesensor", "1.0.4");
|
||||
Homie_setFirmware("etxean-garagesensor", "1.0.5");
|
||||
Homie.setLoopFunction(loopHandler);
|
||||
obstacleNode
|
||||
.advertise("distance").setName("Distance").setDatatype("float").setUnit("m");
|
||||
obstacleNode
|
||||
.advertise("valid").setName("Valid measurement").setDatatype("boolean");
|
||||
obstacleNode
|
||||
.advertise("changed").setName("Obstacle changed").setDatatype("boolean");
|
||||
obstacleNode.setChangeHandler(changeHandler);
|
||||
Homie.setup();
|
||||
}
|
||||
|
||||
@@ -46,86 +51,9 @@ void loop() {
|
||||
Homie.loop();
|
||||
}
|
||||
|
||||
long lastPublish = 0;
|
||||
long lastMeasurement = 0;
|
||||
float lastDistance = 0.0;
|
||||
float validDistance = -1;
|
||||
|
||||
bool checkBounds(float value, float min, float max) {
|
||||
return !isnan(value) && value >= min && value <= max;
|
||||
}
|
||||
|
||||
bool signalChange(float distance, float lastDistance) {
|
||||
return fabs(distance - lastDistance) > minimumChange;
|
||||
}
|
||||
|
||||
String toPayload(bool value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
void loopHandler() {
|
||||
if (millis() - lastMeasurement >= MEASURE_INTERVAL) {
|
||||
float distance = readDistance();
|
||||
//float distance = random(5,400)/100.0;
|
||||
Homie.getLogger() << "Distance: " << distance << " m";
|
||||
bool valid = checkBounds(distance,minDistance,maxDistance);
|
||||
Homie.getLogger() << ", valid = " << toPayload(valid) << endl;
|
||||
if (valid)
|
||||
validDistance = distance;
|
||||
lastMeasurement = millis();
|
||||
}
|
||||
if (millis() - lastPublish >= PUBLISH_INTERVAL) {
|
||||
heartbeat_led();
|
||||
|
||||
bool valid = validDistance > 0;
|
||||
obstacleNode.setProperty("valid").send(toPayload(valid));
|
||||
if (valid)
|
||||
{
|
||||
Homie.getLogger() << "Publish distance: " << validDistance << " m";
|
||||
obstacleNode.setProperty("distance").send(String(validDistance));
|
||||
bool changed = signalChange(validDistance, lastDistance);
|
||||
obstacleNode.setProperty("changed").send(toPayload(changed));
|
||||
if (changed)
|
||||
{
|
||||
signal_led();
|
||||
Homie.getLogger() << ", changed" << endl;
|
||||
}
|
||||
lastDistance = validDistance;
|
||||
validDistance = -1;
|
||||
}
|
||||
lastPublish = millis();
|
||||
}
|
||||
}
|
||||
|
||||
float getEchoTime() {
|
||||
// Clears the trigPin
|
||||
digitalWrite(trigPin, LOW);
|
||||
delayMicroseconds(2);
|
||||
|
||||
// Sets the trigPin on HIGH state for 10 micro seconds
|
||||
digitalWrite(trigPin, HIGH);
|
||||
delayMicroseconds(10);
|
||||
digitalWrite(trigPin, LOW);
|
||||
|
||||
// Reads the echoPin, returns the sound wave travel time in microseconds
|
||||
return pulseIn(echoPin, HIGH);
|
||||
}
|
||||
|
||||
float readDistance() {
|
||||
//auto ping_us = getEchoTime();
|
||||
auto 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
|
||||
auto computedDistance = ping_us*0.0001685;
|
||||
return computedDistance;
|
||||
}
|
||||
|
||||
void signal_led(bool on)
|
||||
{
|
||||
if (on)
|
||||
digitalWrite(ledPin, TURN_ON);
|
||||
else
|
||||
digitalWrite(ledPin, TURN_OFF);
|
||||
digitalWrite(ledPin, on ? TURN_ON : TURN_OFF);
|
||||
}
|
||||
|
||||
void heartbeat_led(int times)
|
||||
|
||||
Reference in New Issue
Block a user