Skip to content

Commit

Permalink
hardware: lightfox fixes & buttonAdd
Browse files Browse the repository at this point in the history
Should be buildable once again

Restore the behaviour from #1468, where button clicks were triggered
Inject relay provider using the relayAdd, add buttonAdd to provide input handling

Remove extra button handling code from the WebUI

Still assuming that DUAL code really needs a syncronized way to handle
input & output with relays, so we can't treat it as a real button.
Without the device it is only guessing, and various issues / comments
are not really clear on that part :/
  • Loading branch information
mcspr committed Jun 9, 2021
1 parent 75b51f1 commit bd3a588
Show file tree
Hide file tree
Showing 9 changed files with 2,404 additions and 2,328 deletions.
23 changes: 12 additions & 11 deletions code/espurna/button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,6 @@ button_t::button_t(BasePinPtr&& pin, const debounce_event::types::Config& config
event_delays(std::move(delays_))
{}

bool button_t::state() {
return event_emitter->isPressed();
}

ButtonEvent button_t::loop() {
if (event_emitter) {
switch (event_emitter->loop()) {
Expand Down Expand Up @@ -371,12 +367,6 @@ void buttonSetEventNotify(ButtonEventHandler handler) {

//------------------------------------------------------------------------------

bool buttonState(size_t id) {
return (id < _buttons.size())
? _buttons[id].state()
: false;
}

ButtonAction buttonAction(size_t id, ButtonEvent event) {
return (id < _buttons.size())
? _buttonDecodeEventAction(_buttons[id].actions, event)
Expand Down Expand Up @@ -780,7 +770,6 @@ bool _buttonSetupProvider(size_t index, ButtonProvider provider) {
bool result { false };

switch (provider) {

case ButtonProvider::Analog:
case ButtonProvider::Gpio: {
#if BUTTON_PROVIDER_GPIO_SUPPORT || BUTTON_PROVIDER_ANALOG_SUPPORT
Expand Down Expand Up @@ -815,6 +804,18 @@ void _buttonSettingsMigrate(int version) {
moveSetting("btnDelay", "btnRepDel");
}

bool buttonAdd() {
const size_t index { buttonCount() };
if ((index + 1) < ButtonsMax) {
_buttons.emplace_back(
_buttonActions(index),
_buttonDelays(index));
return true;
}

return false;
}

void buttonSetup() {
_buttonSettingsMigrate(migrateVersion());

Expand Down
8 changes: 5 additions & 3 deletions code/espurna/button.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ struct ButtonEventDelays {
unsigned long lnglngclick;
};

using ButtonEventEmitterPtr = std::unique_ptr<debounce_event::EventEmitter>;

struct button_t {
button_t(ButtonActions&& actions, ButtonEventDelays&& delays);
button_t(BasePinPtr&& pin, const debounce_event::types::Config& config,
Expand All @@ -87,7 +89,7 @@ struct button_t {
bool state();
ButtonEvent loop();

std::unique_ptr<debounce_event::EventEmitter> event_emitter;
ButtonEventEmitterPtr event_emitter;

ButtonActions actions;
ButtonEventDelays event_delays;
Expand All @@ -97,10 +99,10 @@ using ButtonEventHandler = void(*)(size_t id, ButtonEvent event);
void buttonSetCustomAction(ButtonEventHandler);
void buttonSetNotifyAction(ButtonEventHandler);

bool buttonState(size_t id);
ButtonAction buttonAction(size_t id, const ButtonEvent event);

void buttonEvent(size_t id, ButtonEvent event);

bool buttonAdd();

size_t buttonCount();
void buttonSetup();
18 changes: 12 additions & 6 deletions code/espurna/config/hardware.h
Original file line number Diff line number Diff line change
Expand Up @@ -4294,14 +4294,20 @@
#define SERIAL_BAUDRATE 19200

// Relays
#define RELAY_PROVIDER_DUAL_SUPPORT 1
#define LIGHTFOX_RELAYS 2

#define RELAY1_PROVIDER RELAY_PROVIDER_DUAL
#define RELAY2_PROVIDER RELAY_PROVIDER_DUAL
// Buttons
#define LIGHTFOX_BUTTONS 4

// No need to include generic GPIO support
// "Buttons" are attached to a secondary MCU and RELAY_PROVIDER_DUAL handles that
#define BUTTON_PROVIDER_GPIO_SUPPORT 0
#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_RELAY 2
#define BUTTON3_RELAY 2
#define BUTTON4_RELAY 1

// Conflicts with relay operation
#define DEBUG_SERIAL_SUPPORT 0
Expand Down
Binary file modified code/espurna/data/index.lightfox.html.gz
Binary file not shown.
201 changes: 162 additions & 39 deletions code/espurna/lightfox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,64 +6,147 @@ Copyright (C) 2019 by Andrey F. Kupreychik <foxle@quickfox.ru>
*/

#include "lightfox.h"
#include "espurna.h"

#ifdef FOXEL_LIGHTFOX_DUAL

// -----------------------------------------------------------------------------
// DEFINITIONS
// -----------------------------------------------------------------------------
#include "button.h"
#include "lightfox.h"
#include "relay.h"
#include "terminal.h"
#include "ws.h"

#define LIGHTFOX_CODE_START 0xA0
#define LIGHTFOX_CODE_LEARN 0xF1
#define LIGHTFOX_CODE_CLEAR 0xF2
#define LIGHTFOX_CODE_STOP 0xA1
#include <bitset>
#include <vector>

static_assert(1 == (RELAY_SUPPORT), "");
static_assert(1 == (BUTTON_SUPPORT), "");

constexpr size_t _lightfoxBuildButtons() {
return LIGHTFOX_BUTTONS;
}

constexpr size_t _lightfoxBuildRelays() {
return LIGHTFOX_RELAYS;
}

// -----------------------------------------------------------------------------
// PUBLIC
// PROTOCOL
// -----------------------------------------------------------------------------

void lightfoxLearn() {
Serial.write(LIGHTFOX_CODE_START);
Serial.write(LIGHTFOX_CODE_LEARN);
Serial.write(0x00);
Serial.write(LIGHTFOX_CODE_STOP);
Serial.println();
constexpr uint8_t CodeStart { 0xa0 };
constexpr uint8_t CodeLearn { 0xf1 };
constexpr uint8_t CodeClear { 0xf2 };
constexpr uint8_t CodeStop { 0xa1 };

void _lightfoxSend(uint8_t code) {
uint8_t data[6] {
CodeStart,
code,
0x00,
CodeStop,
static_cast<uint8_t>('\r'),
static_cast<uint8_t>('\n')
};
Serial.write(data, sizeof(data));
Serial.flush();
DEBUG_MSG_P(PSTR("[LIGHTFOX] Learn comman sent\n"));
DEBUG_MSG_P(PSTR("[LIGHTFOX] Code %02X sent\n"), code);
}

void lightfoxLearn() {
_lightfoxSend(CodeLearn);
}

void lightfoxClear() {
Serial.write(LIGHTFOX_CODE_START);
Serial.write(LIGHTFOX_CODE_CLEAR);
Serial.write(0x00);
Serial.write(LIGHTFOX_CODE_STOP);
Serial.println();
Serial.flush();
DEBUG_MSG_P(PSTR("[LIGHTFOX] Clear comman sent\n"));
_lightfoxSend(CodeClear);
}

class LightfoxProvider : public RelayProviderBase {
public:
LightfoxProvider() = delete;
explicit LightfoxProvider(size_t id) :
_id(id)
{
_instances.push_back(this);
}

~LightfoxProvider() {
_instances.erase(
std::remove(_instances.begin(), _instances.end(), this),
_instances.end());
}

const char* id() const override {
return "lightfox";
}

bool setup() override {
static bool once { false };
if (!once) {
once = true;
Serial.begin(SERIAL_BAUDRATE);
}
return true;
}

void change(bool) override {
static bool scheduled { false };
if (!scheduled) {
schedule_function([]() {
flush();
scheduled = false;
});
}
}

size_t relayId() const {
return _id;
}

static std::vector<LightfoxProvider*>& instances() {
return _instances;
}

static void flush() {
size_t mask { 0ul };
for (size_t index = 0; index < _instances.size(); ++index) {
bool status { relayStatus(_instances[index]->relayId()) };
mask |= (status ? 1ul : 0ul << index);
}

DEBUG_MSG_P(PSTR("[LIGHTFOX] Sending DUAL mask: 0x%02X\n"), mask);

uint8_t buffer[4] { 0xa0, 0x04, static_cast<uint8_t>(mask), 0xa1 };
Serial.write(buffer, sizeof(buffer));
Serial.flush();
}

private:
size_t _id;
static std::vector<LightfoxProvider*> _instances;
};

std::vector<LightfoxProvider*> LightfoxProvider::_instances;

size_t _lightfox_button_offset { 0 };
size_t _lightfox_buttons { 0 };

// -----------------------------------------------------------------------------
// WEB
// -----------------------------------------------------------------------------

#if WEB_SUPPORT

void _lightfoxWebSocketOnConnected(JsonObject& root) {
void _lightfoxWebSocketOnVisible(JsonObject& root) {
root["lightfoxVisible"] = 1;
uint8_t buttonsCount = _buttons.size();
root["lightfoxRelayCount"] = relayCount();
JsonArray& rfb = root.createNestedArray("lightfoxButtons");
for (byte id=0; id<buttonsCount; id++) {
JsonObject& node = rfb.createNestedObject();
node["id"] = id;
node["relay"] = getSetting({"btnRelay", id}, 0);
}
}

void _lightfoxWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
if (strcmp(action, "lightfoxLearn") == 0) lightfoxLearn();
if (strcmp(action, "lightfoxClear") == 0) lightfoxClear();
if (strcmp(action, "lightfoxLearn") == 0) {
lightfoxLearn();
} else if (strcmp(action, "lightfoxClear") == 0) {
lightfoxClear();
}
}

#endif
Expand All @@ -76,14 +159,14 @@ void _lightfoxWebSocketOnAction(uint32_t client_id, const char * action, JsonObj

void _lightfoxInitCommands() {

terminalRegisterCommand(F("LIGHTFOX.LEARN"), [](const terminal::CommandContext&) {
terminalRegisterCommand(F("LIGHTFOX.LEARN"), [](const terminal::CommandContext& ctx) {
lightfoxLearn();
DEBUG_MSG_P(PSTR("+OK\n"));
terminalOK(ctx);
});

terminalRegisterCommand(F("LIGHTFOX.CLEAR"), [](const terminal::CommandContext&) {
terminalRegisterCommand(F("LIGHTFOX.CLEAR"), [](const terminal::CommandContext& ctx) {
lightfoxClear();
DEBUG_MSG_P(PSTR("+OK\n"));
terminalOK(ctx);
});
}

Expand All @@ -93,18 +176,58 @@ void _lightfoxInitCommands() {
// SETUP & LOOP
// -----------------------------------------------------------------------------

void _lightfoxInputLoop() {
if (Serial.available() < 4) {
return;
}

unsigned char bytes[4] = {0};
Serial.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
constexpr unsigned long InputsMask { 0xf };
unsigned long mask { static_cast<unsigned long>(bytes[2]) & InputsMask };
unsigned long id { 0 };

for (size_t button = 0; id < _lightfox_buttons; ++button) {
if (mask & (1ul << button)) {
buttonEvent(button + _lightfox_button_offset, ButtonEvent::Click);
}
}
}

void lightfoxSetup() {

#if WEB_SUPPORT
wsRegister()
.onConnected(_lightfoxWebSocketOnConnected)
.onVisible(_lightfoxWebSocketOnVisible)
.onAction(_lightfoxWebSocketOnAction);
#endif

#if TERMINAL_SUPPORT
_lightfoxInitCommands();
#endif

for (size_t relay = 0; relay < _lightfoxBuildRelays(); ++relay) {
size_t relayId { relayCount() };
if (!relayAdd(std::make_unique<LightfoxProvider>(relayId))) {
break;
}
}

_lightfox_button_offset = buttonCount();
for (size_t index = 0; index < _lightfoxBuildButtons(); ++index) {
if (buttonAdd()) {
++_lightfox_buttons;
}
}

espurnaRegisterLoop(_lightfoxInputLoop);

}

#endif
2 changes: 0 additions & 2 deletions code/espurna/lightfox.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@ Copyright (C) 2019 by Andrey F. Kupreychik <foxle@quickfox.ru>
*/

#include "espurna.h"

void lightfoxSetup();
Loading

0 comments on commit bd3a588

Please sign in to comment.