diff --git a/src/components/PushButton.cpp b/src/components/PushButton.cpp new file mode 100644 index 00000000..590910f9 --- /dev/null +++ b/src/components/PushButton.cpp @@ -0,0 +1,150 @@ +#include +#include +#include + +#include "./PushButton.h" + +// Helper function to convert State enum to string for logging +const char *stateToString(PushButton::State state) +{ + switch (state) + { + case PushButton::State::IDLE: + return "IDLE"; + case PushButton::State::PRESSED: + return "PRESSED"; + case PushButton::State::HELD: + return "HELD"; + default: + return "UNKNOWN"; + } +} + +PushButton::PushButton( + Component *owner, + ushort _pin, + short _id, + ushort _modbusAddress) + : NetworkComponent(_modbusAddress, "PushButton", _id, Component::COMPONENT_DEFAULT, owner), + button{_pin, false, 0}, + m_state(this, _id, "State") +{ + setNetCapability(OBJECT_NET_CAPS::E_NCAPS_MODBUS); +} + +short PushButton::setup() +{ + NetworkComponent::setup(); + + pinMode(button.pin, INPUT_PULLUP); + + const uint16_t baseAddr = mb_tcp_base_address(); + + m_state.initNotify(State::IDLE, (State)1, NetworkValue_ThresholdMode::DIFFERENCE); + m_state.initModbus(baseAddr + (ushort)E_MB_Offset::STATE, 1, this->id, this->slaveId, FN_READ_HOLD_REGISTER, "State", this->name.c_str()); + registerBlock(m_state.getRegisterInfo()); + + return E_OK; +} + +unsigned long PushButton::getPressDuration() const +{ + if (pressStartTime > 0) + { + return now - pressStartTime; + } + return 0; +} + +short PushButton::loop() +{ + Component::loop(); + + // Update button state + bool currentlyPressed = (digitalRead(button.pin) == LOW); + if (currentlyPressed && !button.pressed) + { + button.pressTime = now; + } + button.pressed = currentlyPressed; + + State currentState = m_state.getValue(); + State newState = currentState; + + switch (currentState) + { + case State::IDLE: + if (currentlyPressed) + { + if ((now - button.pressTime >= PRESS_TIME_MS)) + { + newState = State::PRESSED; + pressStartTime = button.pressTime; + holdEventTriggered = false; + } + } + break; + + case State::PRESSED: + if (!currentlyPressed) + { + newState = State::IDLE; + pressStartTime = 0; + } + else if (!holdEventTriggered && (now - pressStartTime >= HOLD_TIME_MS)) + { + newState = State::HELD; + holdEventTriggered = true; + } + break; + + case State::HELD: + if (!currentlyPressed) + { + newState = State::IDLE; + pressStartTime = 0; + } + break; + } + + if (newState != currentState) + { + m_state.update(newState, E_PRIORITY::E_PRIORITY_HIGHEST); + Log.verboseln("PushButton::loop, state: %s", stateToString(m_state.getValue())); + notifyStateChange(); + } + + return E_OK; +} + +short PushButton::info(short val0, short val1) +{ + return E_OK; +} + +void PushButton::notifyStateChange() +{ + Component::notifyStateChange(); +} + +short PushButton::mb_tcp_write(MB_Registers *reg, short networkValue) +{ + return E_INVALID_PARAMETER; // All registers are read-only from Modbus perspective +} + +short PushButton::mb_tcp_read(MB_Registers *reg) +{ + short result = NetworkComponent::mb_tcp_read(reg); + if (result != E_NOT_IMPLEMENTED) + { + return result; + } + + uint16_t address = reg->startAddress; + + if (address == (_baseAddress + (ushort)E_MB_Offset::STATE)) + { + return (ushort)m_state.getValue(); + } + return 0; +} diff --git a/src/components/PushButton.h b/src/components/PushButton.h new file mode 100644 index 00000000..9c1d1bd2 --- /dev/null +++ b/src/components/PushButton.h @@ -0,0 +1,75 @@ +#ifndef PUSHBUTTON_H +#define PUSHBUTTON_H + +#include +#include "config.h" +#include +#include "modbus/NetworkComponent.h" +#include "NetworkValue.h" +#include "config-modbus.h" +#include + +#define PUSHBUTTON_MB_COUNT 2 // m_enabled + state + +class Bridge; + +class PushButton : public NetworkComponent +{ +public: + enum class State : ushort + { + IDLE = 0, + PRESSED, + HELD + }; + + enum class E_MB_Offset : ushort + { + STATE = E_NVC_USER + 1, + }; + +private: + static constexpr ushort NB_BUTTONS = 1; + + struct ButtonInfo + { + const ushort pin; + bool pressed; + unsigned long pressTime; + }; + ButtonInfo button; + + NetworkValue m_state; + + // Timing for hold detection + unsigned long pressStartTime = 0; + bool holdEventTriggered = false; + +public: + PushButton( + Component *owner, + ushort _pin, + short _id, + ushort _modbusAddress); + + short setup() override; + short loop() override; + short info(short val0 = 0, short val1 = 0) override; + short debug() override { return info(0, 0); } + + State getState() const { return m_state.getValue(); } + ushort getValue() const { return static_cast(getState()); } + unsigned long getPressDuration() const; + + short mb_tcp_write(MB_Registers *reg, short networkValue) override; + short mb_tcp_read(MB_Registers *reg) override; + +protected: + void notifyStateChange() override; + + // Configuration + static constexpr ushort PRESS_TIME_MS = 50; + static constexpr ushort HOLD_TIME_MS = 1500; +}; + +#endif // PUSHBUTTON_H \ No newline at end of file