140 lines
3.2 KiB
C++
140 lines
3.2 KiB
C++
/*
|
|
* 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);
|
|
}
|
|
}
|
|
|