Browse Source

implement mqtt settings

Maxim Prokhorov 4 years ago
parent
commit
7efa19862d
8 changed files with 188 additions and 242 deletions
  1. +3
    -1
      code/espurna/button.h
  2. +77
    -9
      code/espurna/button.ino
  3. +26
    -0
      code/espurna/button_config.h
  4. +2
    -2
      code/espurna/config/all.h
  5. +64
    -0
      code/espurna/config/defaults.h
  6. +4
    -0
      code/espurna/config/general.h
  7. +12
    -0
      code/espurna/config/types.h
  8. +0
    -230
      code/espurna/debounce.h

+ 3
- 1
code/espurna/button.h View File

@ -8,10 +8,12 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#pragma once #pragma once
#include "debounce.h"
#include "libs/DebounceEvent.h"
#include <memory> #include <memory>
constexpr size_t BUTTONS_MAX = 32;
struct button_event_delays_t { struct button_event_delays_t {
button_event_delays_t(); button_event_delays_t();
button_event_delays_t(unsigned long debounce, unsigned long dblclick, unsigned long lngclick, unsigned long lnglngclick); button_event_delays_t(unsigned long debounce, unsigned long dblclick, unsigned long lngclick, unsigned long lnglngclick);


+ 77
- 9
code/espurna/button.ino View File

@ -8,6 +8,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if BUTTON_SUPPORT #if BUTTON_SUPPORT
#include <bitset>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -18,7 +19,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include "button.h" #include "button.h"
#include "button_config.h" #include "button_config.h"
#include "debounce.h"
#include "libs/DebounceEvent.h"
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -90,10 +91,18 @@ unsigned char buttonCount() {
#if MQTT_SUPPORT #if MQTT_SUPPORT
std::bitset<BUTTONS_MAX> _buttons_mqtt_retain(
(1 == BUTTON_MQTT_RETAIN) ? 0xFFFFFFFFUL : 0UL
);
std::bitset<BUTTONS_MAX> _buttons_mqtt_send_all(
(1 == BUTTON_MQTT_SEND_ALL_EVENTS) ? 0xFFFFFFFFUL : 0UL
);
void buttonMQTT(unsigned char id, uint8_t event) { void buttonMQTT(unsigned char id, uint8_t event) {
char payload[4] = {0}; char payload[4] = {0};
itoa(event, payload, 10); itoa(event, payload, 10);
mqttSend(MQTT_TOPIC_BUTTON, id, payload, false, false); // 1st bool = force, 2nd = retain
// mqttSend(topic, id, payload, force, retail)
mqttSend(MQTT_TOPIC_BUTTON, id, payload, false, _buttons_mqtt_retain[id]);
} }
#endif #endif
@ -106,6 +115,57 @@ void _buttonWebSocketOnVisible(JsonObject& root) {
} }
} }
// XXX: unused! pending webui changes
void _buttonWebSocketOnConnected(JsonObject& root) {
#if 0
if (buttonCount() < 1) return;
JsonObject& module = root.createNestedObject("btn");
// TODO: hardware can sometimes use a different event source
// e.g. Sonoff Dual does not need `Pin`, `Mode` or any of `Del`
// TODO: schema names are uppercase to easily match settings?
// TODO: schema name->type map to generate WebUI elements?
JsonArray& schema = module.createNestedArray("_schema");
schema.add("Pin");
schema.add("Mode");
schema.add("Relay");
schema.add("DebDel");
schema.add("DblDel");
schema.add("LngDel");
schema.add("LngLngDel");
#if MQTT_SUPPORT
schema.add("MqttSnd");
schema.add("MqttRetain");
#endif
JsonArray& buttons = module.createNestedArray("list");
for (unsigned char i=0; i<buttonCount(); i++) {
JsonArray& button = buttons.createNestedArray();
button.add(_buttons[i].pin);
button.add(_buttons[i].mode);
button.add(_buttons[i].relayID);
button.add(_buttons[i].debounceDelay);
button.add(_buttons[i].doubleClickDelay);
button.add(_buttons[i].longClickDelay);
button.add(_buttons[i].longLongClickDelay);
#if MQTT_SUPPORT
button.add(_buttonMqttSendAllEvents(i));
button.add(_buttonMqttRetain(i));
#endif
}
#endif
}
bool _buttonWebSocketOnKeyCheck(const char * key, JsonVariant& value) { bool _buttonWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
return (strncmp(key, "btn", 3) == 0); return (strncmp(key, "btn", 3) == 0);
} }
@ -131,7 +191,7 @@ void buttonEvent(unsigned char id, unsigned char event) {
unsigned char action = _buttonDecodeEventAction(button.actions, event); unsigned char action = _buttonDecodeEventAction(button.actions, event);
#if MQTT_SUPPORT #if MQTT_SUPPORT
if (action != BUTTON_MODE_NONE || BUTTON_MQTT_SEND_ALL_EVENTS) {
if (action != BUTTON_MODE_NONE || _buttons_mqtt_send_all[id]) {
buttonMQTT(id, event); buttonMQTT(id, event);
} }
#endif #endif
@ -248,7 +308,7 @@ void buttonSetup() {
// Generic GPIO input handlers // Generic GPIO input handlers
#else
#elif BUTTON_EVENTS_SOURCE == BUTTON_EVENTS_SOURCE_GENERIC
size_t buttons = 0; size_t buttons = 0;
@ -289,10 +349,10 @@ void buttonSetup() {
} }
button_event_delays_t delays { button_event_delays_t delays {
getSetting({"btnDebDelay", index}, _buttonDebounceDelay(index)),
getSetting({"btnDblCDelay", index}, _buttonDoubleClickDelay(index)),
getSetting({"btnLngCDelay", index}, _buttonLongClickDelay(index)),
getSetting({"btnLngLngCDelay", index}, _buttonLongLongClickDelay(index))
getSetting({"btnDebDel", index}, _buttonDebounceDelay(index)),
getSetting({"btnDblCDel", index}, _buttonDoubleClickDelay(index)),
getSetting({"btnLngCDel", index}, _buttonLongClickDelay(index)),
getSetting({"btnLngLngCDel", index}, _buttonLongLongClickDelay(index))
}; };
// TODO: allow to change DebounceEvent::DigitalPin to something else based on config // TODO: allow to change DebounceEvent::DigitalPin to something else based on config
@ -309,14 +369,22 @@ void buttonSetup() {
DEBUG_MSG_P(PSTR("[BUTTON] Number of buttons: %u\n"), _buttons.size()); DEBUG_MSG_P(PSTR("[BUTTON] Number of buttons: %u\n"), _buttons.size());
#if MQTT_SUPPORT
for (unsigned char index = 0; index < _buttons.size(); ++index) {
_buttons_mqtt_send_all[index] = getSetting({"btnMqttSendAll", index}, _buttonMqttSendAllEvents(index));
_buttons_mqtt_retain[index] = getSetting({"btnMqttRetain", index}, _buttonMqttRetain(index));
}
#endif
// Websocket Callbacks // Websocket Callbacks
#if WEB_SUPPORT #if WEB_SUPPORT
wsRegister() wsRegister()
.onConnected(_buttonWebSocketOnVisible)
.onVisible(_buttonWebSocketOnVisible) .onVisible(_buttonWebSocketOnVisible)
.onKeyCheck(_buttonWebSocketOnKeyCheck); .onKeyCheck(_buttonWebSocketOnKeyCheck);
#endif #endif
// Register loop
// Register system callbacks
espurnaRegisterLoop(buttonLoop); espurnaRegisterLoop(buttonLoop);
} }


+ 26
- 0
code/espurna/button_config.h View File

@ -175,6 +175,32 @@ constexpr const unsigned long _buttonLongLongClickDelay(unsigned char index) {
); );
} }
constexpr const bool _buttonMqttSendAllEvents(unsigned char index) {
return (
(index == 0) ? (1 == BUTTON1_MQTT_SEND_ALL_EVENTS ) :
(index == 1) ? (1 == BUTTON2_MQTT_SEND_ALL_EVENTS ) :
(index == 2) ? (1 == BUTTON3_MQTT_SEND_ALL_EVENTS ) :
(index == 3) ? (1 == BUTTON4_MQTT_SEND_ALL_EVENTS ) :
(index == 4) ? (1 == BUTTON5_MQTT_SEND_ALL_EVENTS ) :
(index == 5) ? (1 == BUTTON6_MQTT_SEND_ALL_EVENTS ) :
(index == 6) ? (1 == BUTTON7_MQTT_SEND_ALL_EVENTS ) :
(index == 7) ? (1 == BUTTON8_MQTT_SEND_ALL_EVENTS ) : (1 == BUTTON_MQTT_SEND_ALL_EVENTS)
);
}
constexpr const bool _buttonMqttRetain(unsigned char index) {
return (
(index == 0) ? (1 == BUTTON1_MQTT_RETAIN ) :
(index == 1) ? (1 == BUTTON2_MQTT_RETAIN ) :
(index == 2) ? (1 == BUTTON3_MQTT_RETAIN ) :
(index == 3) ? (1 == BUTTON4_MQTT_RETAIN ) :
(index == 4) ? (1 == BUTTON5_MQTT_RETAIN ) :
(index == 5) ? (1 == BUTTON6_MQTT_RETAIN ) :
(index == 6) ? (1 == BUTTON7_MQTT_RETAIN ) :
(index == 7) ? (1 == BUTTON8_MQTT_RETAIN ) : (1 == BUTTON_MQTT_RETAIN)
);
}
constexpr const unsigned char _buttonDecodeEventAction(unsigned long actions, unsigned char event) { constexpr const unsigned char _buttonDecodeEventAction(unsigned long actions, unsigned char event) {
return ( return (
(event == BUTTON_EVENT_PRESSED) ? ((actions) & 0x0F) : (event == BUTTON_EVENT_PRESSED) ? ((actions) & 0x0F) :


+ 2
- 2
code/espurna/config/all.h View File

@ -29,14 +29,14 @@
#include "custom.h" #include "custom.h"
#endif #endif
#include "buildtime.h"
#include "version.h" #include "version.h"
#include "types.h" #include "types.h"
#include "arduino.h" #include "arduino.h"
#include "hardware.h" #include "hardware.h"
#include "general.h"
#include "defaults.h" #include "defaults.h"
#include "buildtime.h"
#include "deprecated.h" #include "deprecated.h"
#include "general.h"
#include "dependencies.h" #include "dependencies.h"
#include "sensors.h" #include "sensors.h"
#include "webui.h" #include "webui.h"

+ 64
- 0
code/espurna/config/defaults.h View File

@ -333,6 +333,70 @@
#define BUTTON8_LNGLNGCLICK_DELAY BUTTON_LNGLNGCLICK_DELAY #define BUTTON8_LNGLNGCLICK_DELAY BUTTON_LNGLNGCLICK_DELAY
#endif #endif
#ifndef BUTTON1_MQTT_SEND_ALL_EVENTS
#define BUTTON1_MQTT_SEND_ALL_EVENTS BUTTON_MQTT_SEND_ALL_EVENTS
#endif
#ifndef BUTTON2_MQTT_SEND_ALL_EVENTS
#define BUTTON2_MQTT_SEND_ALL_EVENTS BUTTON_MQTT_SEND_ALL_EVENTS
#endif
#ifndef BUTTON3_MQTT_SEND_ALL_EVENTS
#define BUTTON3_MQTT_SEND_ALL_EVENTS BUTTON_MQTT_SEND_ALL_EVENTS
#endif
#ifndef BUTTON4_MQTT_SEND_ALL_EVENTS
#define BUTTON4_MQTT_SEND_ALL_EVENTS BUTTON_MQTT_SEND_ALL_EVENTS
#endif
#ifndef BUTTON5_MQTT_SEND_ALL_EVENTS
#define BUTTON5_MQTT_SEND_ALL_EVENTS BUTTON_MQTT_SEND_ALL_EVENTS
#endif
#ifndef BUTTON6_MQTT_SEND_ALL_EVENTS
#define BUTTON6_MQTT_SEND_ALL_EVENTS BUTTON_MQTT_SEND_ALL_EVENTS
#endif
#ifndef BUTTON7_MQTT_SEND_ALL_EVENTS
#define BUTTON7_MQTT_SEND_ALL_EVENTS BUTTON_MQTT_SEND_ALL_EVENTS
#endif
#ifndef BUTTON8_MQTT_SEND_ALL_EVENTS
#define BUTTON8_MQTT_SEND_ALL_EVENTS BUTTON_MQTT_SEND_ALL_EVENTS
#endif
#ifndef BUTTON1_MQTT_RETAIN
#define BUTTON1_MQTT_RETAIN BUTTON_MQTT_RETAIN
#endif
#ifndef BUTTON2_MQTT_RETAIN
#define BUTTON2_MQTT_RETAIN BUTTON_MQTT_RETAIN
#endif
#ifndef BUTTON3_MQTT_RETAIN
#define BUTTON3_MQTT_RETAIN BUTTON_MQTT_RETAIN
#endif
#ifndef BUTTON4_MQTT_RETAIN
#define BUTTON4_MQTT_RETAIN BUTTON_MQTT_RETAIN
#endif
#ifndef BUTTON5_MQTT_RETAIN
#define BUTTON5_MQTT_RETAIN BUTTON_MQTT_RETAIN
#endif
#ifndef BUTTON6_MQTT_RETAIN
#define BUTTON6_MQTT_RETAIN BUTTON_MQTT_RETAIN
#endif
#ifndef BUTTON7_MQTT_RETAIN
#define BUTTON7_MQTT_RETAIN BUTTON_MQTT_RETAIN
#endif
#ifndef BUTTON8_MQTT_RETAIN
#define BUTTON8_MQTT_RETAIN BUTTON_MQTT_RETAIN
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Encoders // Encoders
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 4
- 0
code/espurna/config/general.h View File

@ -381,6 +381,10 @@
// 1 - to send all button events to MQTT // 1 - to send all button events to MQTT
#endif #endif
#ifndef BUTTON_MQTT_RETAIN
#define BUTTON_MQTT_RETAIN 1
#endif
#ifndef BUTTON_EVENTS_SOURCE #ifndef BUTTON_EVENTS_SOURCE
#define BUTTON_EVENTS_SOURCE BUTTON_EVENTS_SOURCE_GENERIC // Type of button event source. One of: #define BUTTON_EVENTS_SOURCE BUTTON_EVENTS_SOURCE_GENERIC // Type of button event source. One of:
// BUTTON_EVENTS_SOURCE_GENERIC - GPIOs (virtual or real) // BUTTON_EVENTS_SOURCE_GENERIC - GPIOs (virtual or real)


+ 12
- 0
code/espurna/config/types.h View File

@ -48,6 +48,18 @@
#define BUTTON_EVENTS_SOURCE_ITEAD_SONOFF_DUAL 1 #define BUTTON_EVENTS_SOURCE_ITEAD_SONOFF_DUAL 1
#define BUTTON_EVENTS_SOURCE_FOXEL_LIGHTFOX_DUAL 2 #define BUTTON_EVENTS_SOURCE_FOXEL_LIGHTFOX_DUAL 2
// compat definitions for DebounceEvent
#define BUTTON_PUSHBUTTON DebounceEvent::Types::ModePushbutton
#define BUTTON_SWITCH DebounceEvent::Types::ModeSwitch
#define BUTTON_DEFAULT_HIGH DebounceEvent::Types::ModeDefaultHigh
#define BUTTON_SET_PULLUP DebounceEvent::Types::ModeSetPullup
#define EVENT_NONE DebounceEvent::Types::EventNone
#define EVENT_CHANGED DebounceEvent::Types::EventChanged
#define EVENT_PRESSED DebounceEvent::Types::EventPressed
#define EVENT_RELEASED DebounceEvent::Types::EventReleased
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// ENCODER // ENCODER
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------


+ 0
- 230
code/espurna/debounce.h View File

@ -1,230 +0,0 @@
/*
Original code:
Debounce buttons and trigger events
Copyright (C) 2015-2018 by Xose Pérez <xose dot perez at gmail dot com>
The DebounceEvent library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The DebounceEvent library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the DebounceEvent library. If not, see <http://www.gnu.org/licenses/>.
----------------------------------------------------------------------------------
Modified to include generic INPUT / OUTPUT pin support through a custom interface.
Definitions are incompatible with DebounceEvent, you should not include it's headers.
*/
#pragma once
#include <Arduino.h>
namespace DebounceEvent {
#include <functional>
#include <memory>
namespace Types {
enum event_t {
EventNone,
EventChanged,
EventPressed,
EventReleased
};
enum mode_t {
ModePushbutton = 1 << 0,
ModeSwitch = 1 << 1,
ModeDefaultHigh = 1 << 2,
ModeSetPullup = 1 << 3
};
}
constexpr const unsigned long DebounceDelay = 50UL;
constexpr const unsigned long RepeatDelay = 500UL;
// base interface for generic pin handler.
class PinBase {
public:
PinBase(unsigned char pin) :
pin(pin)
{}
virtual void pinMode(int8_t mode) = 0;
virtual void digitalWrite(int8_t val) = 0;
virtual int digitalRead() = 0;
const unsigned char pin;
};
// real hardware pin
class DigitalPin : public PinBase {
public:
DigitalPin(unsigned char pin) :
PinBase(pin)
{}
void pinMode(int8_t mode) {
// Note: proxy for pinMode so it doesn't ignore INPUT_PULLUP with GPIO16
if (((mode == INPUT) || (mode == INPUT_PULLUP)) && this->pin == 16) {
::pinMode(this->pin, ((mode == INPUT_PULLUP) ? INPUT : INPUT_PULLDOWN_16));
return;
}
::pinMode(this->pin, mode);
}
void digitalWrite(int8_t val) {
::digitalWrite(this->pin, val);
}
int digitalRead() {
return ::digitalRead(this->pin);
}
};
class DebounceEvent {
public:
// TODO: not used in espurna buttons node
using callback_f = std::function<void(DebounceEvent* self, uint8_t event, uint8_t count, uint16_t length)>;
DebounceEvent(std::shared_ptr<PinBase> pin, int mode = Types::ModePushbutton | Types::ModeDefaultHigh, unsigned long delay = DebounceDelay, unsigned long repeat = RepeatDelay);
DebounceEvent(std::shared_ptr<PinBase> pin, callback_f callback, int mode = Types::ModePushbutton | Types::ModeDefaultHigh, unsigned long delay = DebounceDelay, unsigned long repeat = RepeatDelay);
Types::event_t loop();
bool pressed();
unsigned long getEventLength();
unsigned long getEventCount();
std::shared_ptr<PinBase> pin;
callback_f callback;
const int mode;
private:
const bool _is_switch;
const bool _default_status;
const unsigned long _delay;
const unsigned long _repeat;
bool _status;
bool _ready;
bool _reset_count;
unsigned long _event_start;
unsigned long _event_length;
unsigned char _event_count;
};
DebounceEvent::DebounceEvent(std::shared_ptr<PinBase> pin, DebounceEvent::callback_f callback, int mode, unsigned long debounce_delay, unsigned long repeat) :
pin(pin),
callback(callback),
mode(mode),
_is_switch(mode & Types::ModeSwitch),
_default_status(mode & Types::ModeDefaultHigh),
_delay(debounce_delay),
_repeat(repeat),
_status(false),
_ready(false),
_reset_count(true),
_event_start(0),
_event_length(0),
_event_count(0)
{
pin->pinMode((mode & Types::ModeSetPullup) ? INPUT_PULLUP : INPUT);
_status = (mode & Types::ModeSwitch) ? pin->digitalRead() : _default_status;
}
DebounceEvent::DebounceEvent(std::shared_ptr<PinBase> pin, int mode, unsigned long delay, unsigned long repeat) :
DebounceEvent(pin, nullptr, mode, delay, repeat)
{}
bool DebounceEvent::pressed() {
return (_status != _default_status);
}
unsigned long DebounceEvent::getEventLength() {
return _event_length;
}
unsigned long DebounceEvent::getEventCount() {
return _event_count;
}
Types::event_t DebounceEvent::loop() {
auto event = Types::EventNone;
if (pin->digitalRead() != _status) {
// TODO: check each loop instead of blocking?
auto start = millis();
while (millis() - start < _delay) delay(1);
if (pin->digitalRead() != _status) {
_status = !_status;
if (_is_switch) {
event = Types::EventChanged;
} else {
if (_status == _default_status) {
_event_length = millis() - _event_start;
_ready = true;
} else {
event = Types::EventPressed;
_event_start = millis();
_event_length = 0;
if (_reset_count) {
_event_count = 1;
_reset_count = false;
} else {
++_event_count;
}
_ready = false;
}
}
}
}
if (_ready && (millis() - _event_start > _repeat)) {
_ready = false;
_reset_count = true;
event = Types::EventReleased;
}
if (callback && (event != Types::EventNone)) {
callback(this, event, _event_count, _event_length);
}
return event;
}
// compat definitions from the original lib
#define BUTTON_PUSHBUTTON DebounceEvent::Types::ModePushbutton
#define BUTTON_SWITCH DebounceEvent::Types::ModeSwitch
#define BUTTON_DEFAULT_HIGH DebounceEvent::Types::ModeDefaultHigh
#define BUTTON_SET_PULLUP DebounceEvent::Types::ModeSetPullup
#define EVENT_NONE DebounceEvent::Types::EventNone
#define EVENT_CHANGED DebounceEvent::Types::EventChanged
#define EVENT_PRESSED DebounceEvent::Types::EventPressed
#define EVENT_RELEASED DebounceEvent::Types::EventReleased
} // namespace DebounceEvent

Loading…
Cancel
Save