Browse Source

lightfox: button provider experiments

another pin provider, similar to analogpin
instead of generating events, use last serial reading (with a timeout)
pull/2575/head
Maxim Prokhorov 1 year ago
parent
commit
dca13574d9
6 changed files with 127 additions and 62 deletions
  1. +23
    -20
      code/espurna/button.cpp
  2. +0
    -2
      code/espurna/button.h
  3. +10
    -6
      code/espurna/config/hardware.h
  4. +2
    -0
      code/espurna/config/types.h
  5. +89
    -34
      code/espurna/lightfox.cpp
  6. +3
    -0
      code/espurna/lightfox.h

+ 23
- 20
code/espurna/button.cpp View File

@ -16,6 +16,7 @@ Copyright (C) 2019-2021 by Maxim Prokhorov <prokhorov dot max at outlook dot com
#include "fan.h"
#include "gpio.h"
#include "light.h"
#include "lightfox.h"
#include "mqtt.h"
#include "relay.h"
#include "system.h"
@ -35,7 +36,8 @@ enum class ButtonProvider {
None,
Dummy,
Gpio,
Analog
Analog,
Lightfox,
};
struct ButtonActions {
@ -147,12 +149,14 @@ PROGMEM_STRING(None, "none");
PROGMEM_STRING(Dummy, "dummy");
PROGMEM_STRING(Gpio, "gpio");
PROGMEM_STRING(Analog, "analog");
PROGMEM_STRING(Lightfox, "lightfox");
static constexpr std::array<Enumeration<ButtonProvider>, 4> ButtonProviderOptions PROGMEM {
static constexpr std::array<Enumeration<ButtonProvider>, 5> ButtonProviderOptions PROGMEM {
{{ButtonProvider::None, None},
{ButtonProvider::Dummy, Dummy},
{ButtonProvider::Gpio, Gpio},
{ButtonProvider::Analog, Analog}}
{ButtonProvider::Analog, Analog},
{ButtonProvider::Lightfox, Lightfox}}
};
[[gnu::unused]] PROGMEM_STRING(Toggle, "relay-toggle");
@ -1418,6 +1422,14 @@ ButtonEventDelays _buttonDelays(size_t index) {
.lnglngclick = espurna::button::settings::longLongClickDelay(index)};
}
void _buttonAddWithPin(size_t index, BasePinPtr&& pin) {
espurna::button::internal::buttons.emplace_back(
std::move(pin),
_buttonRuntimeConfig(index),
_buttonActions(index),
_buttonDelays(index));
}
bool _buttonSetupProvider(size_t index, ButtonProvider provider) {
bool result { false };
@ -1437,16 +1449,19 @@ bool _buttonSetupProvider(size_t index, ButtonProvider provider) {
break;
}
espurna::button::internal::buttons.emplace_back(
std::move(pin),
_buttonRuntimeConfig(index),
_buttonActions(index),
_buttonDelays(index));
_buttonAddWithPin(index, std::move(pin));
result = true;
#endif
break;
}
case ButtonProvider::Lightfox:
#ifdef FOXEL_LIGHTFOX_DUAL
_buttonAddWithPin(index, lightfoxMakeButtonPin(index));
result = true;
#endif
break;
case ButtonProvider::None:
break;
@ -1464,18 +1479,6 @@ void _buttonSettingsMigrate(int version) {
} // namespace
bool buttonAdd() {
const size_t index { buttonCount() };
if ((index + 1) < ButtonsMax) {
espurna::button::internal::buttons.emplace_back(
_buttonActions(index),
_buttonDelays(index));
return true;
}
return false;
}
void buttonSetup() {
migrateVersion(_buttonSettingsMigrate);
espurna::button::settings::query::setup();


+ 0
- 2
code/espurna/button.h View File

@ -57,7 +57,5 @@ ButtonAction buttonAction(size_t id, const ButtonEvent event);
void buttonEvent(size_t id, ButtonEvent event);
void buttonOnEvent(ButtonEventHandler);
bool buttonAdd();
size_t buttonCount();
void buttonSetup();

+ 10
- 6
code/espurna/config/hardware.h View File

@ -4497,16 +4497,20 @@
#define RELAY2_PROVIDER RELAY_PROVIDER_LIGHTFOX
// Buttons
#define LIGHTFOX_BUTTONS 4
#define BUTTON1_PROVIDER BUTTON_PROVIDER_LIGHTFOX
#define BUTTON1_CLICK BUTTON_ACTION_TOGGLE
#define BUTTON2_CLICK BUTTON_ACTION_TOGGLE
#define BUTTON3_CLICK BUTTON_ACTION_TOGGLE
#define BUTTON4_CLICK BUTTON_ACTION_TOGGLE
#define BUTTON1_RELAY 1
#define BUTTON2_PROVIDER BUTTON_PROVIDER_LIGHTFOX
#define BUTTON2_CLICK BUTTON_ACTION_TOGGLE
#define BUTTON2_RELAY 2
#define BUTTON3_PROVIDER BUTTON_PROVIDER_LIGHTFOX
#define BUTTON3_CLICK BUTTON_ACTION_TOGGLE
#define BUTTON3_RELAY 2
#define BUTTON4_PROVIDER BUTTON_PROVIDER_LIGHTFOX
#define BUTTON4_CLICK BUTTON_ACTION_TOGGLE
#define BUTTON4_RELAY 1
// -----------------------------------------------------------------------------


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

@ -61,8 +61,10 @@
// configure where do we get the button events
#define BUTTON_PROVIDER_NONE ButtonProvider::None
#define BUTTON_PROVIDER_DUMMY ButtonProvider::Dummy
#define BUTTON_PROVIDER_GPIO ButtonProvider::Gpio
#define BUTTON_PROVIDER_ANALOG ButtonProvider::Analog
#define BUTTON_PROVIDER_LIGHTFOX ButtonProvider::Lightfox
//------------------------------------------------------------------------------
// ENCODER


+ 89
- 34
code/espurna/lightfox.cpp View File

@ -22,10 +22,6 @@ static_assert(1 == (BUTTON_SUPPORT), "");
#include <array>
#include <vector>
#ifndef LIGHTFOX_BUTTONS
#define LIGHTFOX_BUTTONS 4
#endif
#ifndef LIGHTFOX_PORT
#define LIGHTFOX_PORT 1
#endif
@ -37,10 +33,6 @@ namespace {
namespace build {
constexpr size_t buttons() {
return LIGHTFOX_BUTTONS;
}
constexpr size_t port() {
return LIGHTFOX_PORT - 1;
}
@ -53,9 +45,6 @@ namespace internal {
Stream* port { nullptr };
size_t button_offset { 0 };
size_t buttons { 0 };
} // namespace internal
constexpr uint8_t CodeStart { 0xa0 };
@ -213,30 +202,99 @@ void setup() {
// -----------------------------------------------------------------------------
void loop() {
if (internal::port->available() < 4) {
return;
class ButtonPin final : public BasePin {
public:
ButtonPin() = delete;
explicit ButtonPin(size_t index) :
_index(index)
{
_readings.push_back(Reading{});
}
unsigned char bytes[4] = {0};
internal::port->readBytes(bytes, 4);
if ((bytes[0] != 0xA0) && (bytes[1] != 0x04) && (bytes[3] != 0xA1)) {
return;
String description() const override {
String out;
out += STRING_VIEW("lightfox id:");
out += _index;
out += STRING_VIEW(" status:#");
out += _readings[_index].status ? 't' : 'f';
return out;
}
// Unlike DUAL, inputs may have different IDs than the outputs
// ref. https://github.com/foxel/esp-dual-rf-switch
constexpr unsigned long InputsMask { 0xf };
unsigned long mask { static_cast<unsigned long>(bytes[2]) & InputsMask };
unsigned long id { 0 };
static void loop() {
const auto now = TimeSource::now();
for (size_t button = 0; id < internal::buttons; ++button) {
if (mask & (1ul << button)) {
buttonEvent(button + internal::button_offset, ButtonEvent::Click);
// Emulate 'Click' behaviour by expiring our readings
// But, unlike previous version, we could make either a switch or a button
for (auto& reading : _readings) {
if (reading.status && ((now - reading.last) > ReadInterval)) {
reading.status = false;
}
}
if (internal::port->available() < 4) {
return;
}
uint8_t bytes[4] = {0};
internal::port->readBytes(bytes, 4);
if ((bytes[0] != 0xA0) && (bytes[1] != 0x04) && (bytes[3] != 0xA1)) {
return;
}
// Unlike DUAL, inputs may have different IDs than the outputs
// ref. https://github.com/foxel/esp-dual-rf-switch
static constexpr uint8_t Digits { std::numeric_limits<uint8_t>::digits };
const auto mask = bytes[2];
for (uint8_t index = 0; index < Digits; ++index) {
if (((mask & index) > 0) && (index < _readings.size())) {
_readings[index].status = true;
_readings[index].last = now;
}
}
}
unsigned char pin() const override {
return _index;
}
const char* id() const override {
return "LightfoxPin";
}
// Simulate LOW level when the range matches and HIGH when it does not
int digitalRead() override {
return _readings[_index].status;
}
void pinMode(int8_t) override {
}
void digitalWrite(int8_t val) override {
}
private:
using TimeSource = time::SystemClock;
static constexpr TimeSource::duration ReadInterval
= duration::Milliseconds{ 100 };
struct Reading {
bool status { false };
TimeSource::time_point last;
};
size_t _index;
static std::vector<Reading> _readings;
};
BasePinPtr make_button(size_t index) {
return std::make_unique<ButtonPin>(index);
}
std::vector<ButtonPin::Reading> ButtonPin::_readings;
void setup() {
const auto port = uartPort(build::port());
if (!port) {
@ -252,14 +310,7 @@ void setup() {
terminal::setup();
#endif
internal::button_offset = buttonCount();
for (size_t index = 0; index < build::buttons(); ++index) {
if (buttonAdd()) {
++internal::buttons;
}
}
::espurnaRegisterLoop(lightfox::loop);
::espurnaRegisterLoop(ButtonPin::loop);
}
} // namespace
@ -267,6 +318,10 @@ void setup() {
} // namespace hardware
} // namespace espurna
BasePinPtr lightfoxMakeButtonPin(size_t index) {
return espurna::hardware::lightfox::make_button(index);
}
RelayProviderBasePtr lightfoxMakeRelayProvider(size_t index) {
return espurna::hardware::lightfox::make_relay(index);
}


+ 3
- 0
code/espurna/lightfox.h View File

@ -14,4 +14,7 @@ Copyright (C) 2019 by Andrey F. Kupreychik <foxle@quickfox.ru>
class RelayProviderBase;
std::unique_ptr<RelayProviderBase> lightfoxMakeRelayProvider(size_t);
class BasePin;
std::unique_ptr<BasePin> lightfoxMakeButtonPin(size_t);
void lightfoxSetup();

Loading…
Cancel
Save