Updated with reed relais and debouncing button
This commit is contained in:
139
lib/HomieNodes/ButtonNode.cpp
Normal file
139
lib/HomieNodes/ButtonNode.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* ButtonNode.cpp
|
||||
* Homie Node for a button with optional callback function
|
||||
*
|
||||
* Version: 1.0
|
||||
* Author: Lübbe Onken (http://github.com/luebbe)
|
||||
*/
|
||||
|
||||
#include "ButtonNode.hpp"
|
||||
|
||||
ButtonNode::ButtonNode(const char *name,
|
||||
const int buttonPin,
|
||||
TButtonPressCallback buttonPressCallback,
|
||||
TButtonChangeCallback buttonChangeCallback)
|
||||
: HomieNode(name, "ButtonNode", "sensor")
|
||||
{
|
||||
_buttonPin = buttonPin;
|
||||
_buttonPressCallback = buttonPressCallback;
|
||||
_buttonChangeCallback = buttonChangeCallback;
|
||||
}
|
||||
|
||||
void ButtonNode::handleButtonPress(unsigned long dt)
|
||||
{
|
||||
if (Homie.isConnected())
|
||||
{
|
||||
setProperty("duration").send(String(dt));
|
||||
}
|
||||
|
||||
printCaption();
|
||||
Homie.getLogger() << cIndent << "pressed: " << dt << " ms" << endl;
|
||||
|
||||
if (_buttonPressCallback)
|
||||
{
|
||||
_buttonPressCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonNode::handleButtonChange(bool down) {
|
||||
if (Homie.isConnected())
|
||||
{
|
||||
setProperty("down").send(down ? "true" : "false");
|
||||
}
|
||||
|
||||
printCaption();
|
||||
Homie.getLogger() << cIndent << (down ? "down" : "up") << endl;
|
||||
|
||||
if (_buttonChangeCallback)
|
||||
{
|
||||
_buttonChangeCallback(down);
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonNode::onPress(TButtonPressCallback buttonCallback)
|
||||
{
|
||||
_buttonPressCallback = buttonCallback;
|
||||
}
|
||||
|
||||
void ButtonNode::onChange(TButtonChangeCallback buttonCallback)
|
||||
{
|
||||
_buttonChangeCallback = buttonCallback;
|
||||
}
|
||||
|
||||
void ButtonNode::setMinButtonDownTime(unsigned short downTime)
|
||||
{
|
||||
_minButtonDownTime = downTime;
|
||||
}
|
||||
|
||||
void ButtonNode::setMaxButtonDownTime(unsigned short downTime)
|
||||
{
|
||||
_maxButtonDownTime = downTime;
|
||||
}
|
||||
|
||||
void ButtonNode::printCaption()
|
||||
{
|
||||
Homie.getLogger() << cCaption << endl;
|
||||
}
|
||||
|
||||
void ButtonNode::loop()
|
||||
{
|
||||
// Detect a single press between 90ms and 900ms
|
||||
// This could be improved to detect multiple quick or long presses
|
||||
// and:
|
||||
// a) report them via MQTT
|
||||
// b) react by calling different callbacks
|
||||
if (_buttonPin > DEFAULTPIN)
|
||||
{
|
||||
byte reading = digitalRead(_buttonPin);
|
||||
|
||||
if (reading != _lastReading) {
|
||||
// reset the debouncing timer
|
||||
_lastDebounceTime = millis();
|
||||
}
|
||||
|
||||
if ((millis() - _lastDebounceTime) > _minButtonDownTime) {
|
||||
// whatever the reading is at, it's been there for longer than the debounce
|
||||
// delay, so take it as the actual current state:
|
||||
if (reading != _buttonState)
|
||||
{
|
||||
_buttonState = reading;
|
||||
|
||||
if (_buttonState == LOW)
|
||||
{
|
||||
handleButtonChange(true);
|
||||
_buttonChangeHandled = true;
|
||||
_buttonDownTime = millis();
|
||||
_buttonPressHandled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
handleButtonChange(false);
|
||||
_buttonChangeHandled = true;
|
||||
|
||||
unsigned long dt = millis() - _buttonDownTime;
|
||||
if (dt >= _minButtonDownTime && dt <= _maxButtonDownTime && !_buttonPressHandled)
|
||||
{
|
||||
handleButtonPress(dt);
|
||||
_buttonPressHandled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_lastReading = reading;
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonNode::setup()
|
||||
{
|
||||
advertise("down").setDatatype("boolean");
|
||||
advertise("duration").setDatatype("integer").setUnit("ms");
|
||||
|
||||
printCaption();
|
||||
Homie.getLogger() << cIndent << "Pin: " << _buttonPin << endl;
|
||||
|
||||
if (_buttonPin > DEFAULTPIN)
|
||||
{
|
||||
pinMode(_buttonPin, INPUT_PULLUP);
|
||||
}
|
||||
}
|
||||
|
||||
52
lib/HomieNodes/ButtonNode.hpp
Normal file
52
lib/HomieNodes/ButtonNode.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* ButtonNode.hpp
|
||||
* Homie Node for a button with optional callback function
|
||||
*
|
||||
* Version: 1.0
|
||||
* Author: Lübbe Onken (http://github.com/luebbe)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Homie.hpp>
|
||||
|
||||
#define DEFAULTPIN -1
|
||||
|
||||
class ButtonNode : public HomieNode
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(void)> TButtonPressCallback;
|
||||
typedef std::function<void(bool)> TButtonChangeCallback;
|
||||
|
||||
|
||||
private:
|
||||
const char *cCaption = "• Button:";
|
||||
const char *cIndent = " ◦ ";
|
||||
|
||||
TButtonPressCallback _buttonPressCallback;
|
||||
TButtonChangeCallback _buttonChangeCallback;
|
||||
int _buttonPin;
|
||||
byte _lastReading = HIGH;
|
||||
byte _buttonState = HIGH;
|
||||
bool _buttonPressHandled = 0;
|
||||
bool _buttonChangeHandled = 0;
|
||||
unsigned long _buttonDownTime = 0;
|
||||
unsigned long _minButtonDownTime = 90;
|
||||
unsigned long _maxButtonDownTime = 2000;
|
||||
unsigned long _lastDebounceTime = 0; // the last time the button pin was toggled
|
||||
|
||||
void handleButtonPress(unsigned long dt);
|
||||
void handleButtonChange(bool down);
|
||||
void printCaption();
|
||||
|
||||
protected:
|
||||
virtual void loop() override;
|
||||
virtual void setup() override;
|
||||
|
||||
public:
|
||||
explicit ButtonNode(const char *name, const int buttonPin = DEFAULTPIN, TButtonPressCallback buttonPressedCallback = NULL, TButtonChangeCallback buttonChangedCallback = NULL);
|
||||
void onPress(TButtonPressCallback buttonCallback);
|
||||
void onChange(TButtonChangeCallback buttonCallback);
|
||||
void setMinButtonDownTime(unsigned short downTime);
|
||||
void setMaxButtonDownTime(unsigned short downTime);
|
||||
};
|
||||
111
lib/HomieNodes/ContactNode.cpp
Normal file
111
lib/HomieNodes/ContactNode.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* ContactNode.cpp
|
||||
* Homie Node for a Contact switch
|
||||
*
|
||||
* Version: 1.0
|
||||
* Author: Lübbe Onken (http://github.com/luebbe)
|
||||
*/
|
||||
|
||||
#include "ContactNode.hpp"
|
||||
|
||||
ContactNode::ContactNode(const char *name,
|
||||
const int contactPin,
|
||||
TContactCallback contactCallback)
|
||||
: HomieNode(name, "ContactNode", "sensor")
|
||||
{
|
||||
_contactPin = contactPin;
|
||||
_contactCallback = contactCallback;
|
||||
}
|
||||
|
||||
int ContactNode::getContactPin()
|
||||
{
|
||||
return _contactPin;
|
||||
}
|
||||
|
||||
byte ContactNode::readPin()
|
||||
{
|
||||
return digitalRead(_contactPin);
|
||||
}
|
||||
|
||||
// Debounce input pin.
|
||||
bool ContactNode::debouncePin(void)
|
||||
{
|
||||
byte inputState = readPin();
|
||||
if (inputState != _lastInputState)
|
||||
{
|
||||
_stateChangedTime = millis();
|
||||
_stateChangeHandled = false;
|
||||
_lastInputState = inputState;
|
||||
#ifdef DEBUG
|
||||
Homie.getLogger() << "State Changed to " << inputState << endl;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long dt = millis() - _stateChangedTime;
|
||||
if (dt >= DEBOUNCE_TIME && !_stateChangeHandled)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Homie.getLogger() << "State Stable for " << dt << "ms" << endl;
|
||||
#endif
|
||||
_stateChangeHandled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ContactNode::handleStateChange(bool open)
|
||||
{
|
||||
if (Homie.isConnected())
|
||||
{
|
||||
setProperty("open").send(open ? "true" : "false");
|
||||
}
|
||||
if (_contactCallback)
|
||||
{
|
||||
_contactCallback(open);
|
||||
}
|
||||
|
||||
printCaption();
|
||||
Homie.getLogger() << cIndent << "is " << (open ? "open" : "closed") << endl;
|
||||
}
|
||||
|
||||
void ContactNode::onChange(TContactCallback contactCallback)
|
||||
{
|
||||
_contactCallback = contactCallback;
|
||||
}
|
||||
|
||||
void ContactNode::printCaption()
|
||||
{
|
||||
Homie.getLogger() << cCaption << endl;
|
||||
}
|
||||
|
||||
void ContactNode::loop()
|
||||
{
|
||||
if (_contactPin > DEFAULTPIN)
|
||||
{
|
||||
if (debouncePin() && (_lastSentState != _lastInputState))
|
||||
{
|
||||
handleStateChange(_lastInputState == HIGH);
|
||||
_lastSentState = _lastInputState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContactNode::setupPin()
|
||||
{
|
||||
pinMode(_contactPin, INPUT_PULLUP);
|
||||
}
|
||||
|
||||
void ContactNode::setup()
|
||||
{
|
||||
advertise("open");
|
||||
|
||||
printCaption();
|
||||
Homie.getLogger() << cIndent << "Pin: " << _contactPin << endl;
|
||||
|
||||
if (_contactPin > DEFAULTPIN)
|
||||
{
|
||||
setupPin();
|
||||
}
|
||||
}
|
||||
47
lib/HomieNodes/ContactNode.hpp
Normal file
47
lib/HomieNodes/ContactNode.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* ContactNode.hpp
|
||||
* Homie Node for a Contact switch
|
||||
*
|
||||
* Version: 1.0
|
||||
* Author: Lübbe Onken (http://github.com/luebbe)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Homie.hpp>
|
||||
|
||||
#define DEFAULTPIN -1
|
||||
#define DEBOUNCE_TIME 200
|
||||
|
||||
class ContactNode : public HomieNode
|
||||
{
|
||||
public:
|
||||
typedef std::function<void(bool)> TContactCallback;
|
||||
|
||||
private:
|
||||
const char *cCaption = "• Contact:";
|
||||
const char *cIndent = " ◦ ";
|
||||
|
||||
TContactCallback _contactCallback;
|
||||
int _contactPin;
|
||||
// Use invalid values for last states to force sending initial state...
|
||||
int _lastInputState = -1; // Input pin state.
|
||||
int _lastSentState = -1; // Last pin state sent
|
||||
bool _stateChangeHandled = false;
|
||||
unsigned long _stateChangedTime = 0;
|
||||
|
||||
bool debouncePin(void);
|
||||
void handleStateChange(bool open);
|
||||
void printCaption();
|
||||
|
||||
protected:
|
||||
int getContactPin();
|
||||
virtual void loop() override;
|
||||
virtual void setup() override;
|
||||
virtual void setupPin();
|
||||
virtual byte readPin();
|
||||
|
||||
public:
|
||||
explicit ContactNode(const char *name, const int contactPin = DEFAULTPIN, TContactCallback contactCallback = NULL);
|
||||
void onChange(TContactCallback contactCallback);
|
||||
};
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* 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(cPingTopic).send(String(_ping_us));
|
||||
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*_microseconds2meter;
|
||||
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;
|
||||
printCaption();
|
||||
Homie.getLogger() << cIndent
|
||||
<< "SpeedOfSound: " << soundSpeed << " " << cUnitMetersPerSecond
|
||||
<< " at " << temperatureCelcius << " " << cUnitDegrees << endl;
|
||||
// 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;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 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 = 0;
|
||||
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);
|
||||
};
|
||||
Reference in New Issue
Block a user