Skip to content

Commit

Permalink
lightfox: button provider experiments
Browse files Browse the repository at this point in the history
another pin provider, similar to analogpin
instead of generating events, use last serial reading (with a timeout)
  • Loading branch information
mcspr committed Jan 11, 2023
1 parent f373e13 commit dca1357
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 62 deletions.
43 changes: 23 additions & 20 deletions code/espurna/button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -35,7 +36,8 @@ enum class ButtonProvider {
None,
Dummy,
Gpio,
Analog
Analog,
Lightfox,
};

struct ButtonActions {
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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 };

Expand All @@ -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;

Expand All @@ -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();
Expand Down
2 changes: 0 additions & 2 deletions code/espurna/button.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
16 changes: 10 additions & 6 deletions code/espurna/config/hardware.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

// -----------------------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions code/espurna/config/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
123 changes: 89 additions & 34 deletions code/espurna/lightfox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -37,10 +33,6 @@ namespace {

namespace build {

constexpr size_t buttons() {
return LIGHTFOX_BUTTONS;
}

constexpr size_t port() {
return LIGHTFOX_PORT - 1;
}
Expand All @@ -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 };
Expand Down Expand Up @@ -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) {
Expand All @@ -252,21 +310,18 @@ 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
} // namespace lightfox
} // 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);
}
Expand Down
3 changes: 3 additions & 0 deletions code/espurna/lightfox.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

0 comments on commit dca1357

Please sign in to comment.