Add PushButton component with Modbus state register and hold logic

This commit is contained in:
babayaga 2026-01-17 06:25:45 +01:00
parent 357e36b24d
commit 2d9815b199
2 changed files with 225 additions and 0 deletions

View File

@ -0,0 +1,150 @@
#include <App.h>
#include <Bridge.h>
#include <modbus/Modbus.h>
#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;
}

View File

@ -0,0 +1,75 @@
#ifndef PUSHBUTTON_H
#define PUSHBUTTON_H
#include <ArduinoLog.h>
#include "config.h"
#include <App.h>
#include "modbus/NetworkComponent.h"
#include "NetworkValue.h"
#include "config-modbus.h"
#include <stdint.h>
#define PUSHBUTTON_MB_COUNT 2 // m_enabled + state
class Bridge;
class PushButton : public NetworkComponent<PUSHBUTTON_MB_COUNT>
{
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<State> 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<ushort>(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