Refactor GatewayModbus and GatewayNetwork components

- Updated GatewayModbusConfig to allow uart_port and pin values to be -1, indicating an unconfigured state.
- Enhanced GatewayNetworkService to support an additional setup AP button with configurable GPIO and active low settings.
- Refactored boot button configuration logic to reduce redundancy and improve clarity.
- Introduced a new method for handling GPIO input configuration.
- Improved boot button task loop to handle both boot and setup AP buttons more effectively.
- Added programming mode functionality to EtsDeviceRuntime, allowing toggling and querying of the programming state.
- Implemented memory checks to avoid unnecessary reads in EtsDeviceRuntime.
- Enhanced security storage to derive factory FDSK from the device's serial number and store it in NVS.
- Updated factory FDSK loading logic to ensure proper key generation and storage.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-13 12:36:16 +08:00
parent df1dd472cc
commit b74367e5a0
18 changed files with 2244 additions and 609 deletions
+83 -7
View File
@@ -10,12 +10,14 @@
#include "lwip/sockets.h"
#include <atomic>
#include <array>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
@@ -62,6 +64,10 @@ struct GatewayKnxConfig {
uint16_t udp_port{kGatewayKnxDefaultUdpPort};
std::string multicast_address{kGatewayKnxDefaultMulticastAddress};
uint16_t individual_address{0x1101};
int programming_button_gpio{-1};
bool programming_button_active_low{true};
int programming_led_gpio{-1};
bool programming_led_active_high{true};
std::vector<GatewayKnxEtsAssociation> ets_associations;
GatewayKnxTpUartConfig tp_uart;
};
@@ -111,6 +117,7 @@ struct GatewayKnxCommissioningBallast {
std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value);
DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config);
bool GatewayKnxConfigUsesTpUart(const GatewayKnxConfig& config);
const char* GatewayKnxMappingModeToString(GatewayKnxMappingMode mode);
GatewayKnxMappingMode GatewayKnxMappingModeFromString(const std::string& value);
@@ -132,6 +139,8 @@ class GatewayKnxBridge {
size_t etsBindingCount() const;
std::vector<GatewayKnxDaliBinding> describeDaliBindings() const;
bool matchesCemiFrame(const uint8_t* data, size_t len) const;
bool matchesGroupAddress(uint16_t group_address) const;
DaliBridgeResult handleCemiFrame(const uint8_t* data, size_t len);
DaliBridgeResult handleGroupWrite(uint16_t group_address, const uint8_t* data,
size_t len);
@@ -186,13 +195,19 @@ class GatewayKnxBridge {
class GatewayKnxTpIpRouter {
public:
using CemiFrameHandler = std::function<DaliBridgeResult(const uint8_t* data, size_t len)>;
using GroupWriteHandler = std::function<DaliBridgeResult(uint16_t group_address,
const uint8_t* data,
size_t len)>;
GatewayKnxTpIpRouter(GatewayKnxBridge& bridge, CemiFrameHandler handler,
std::string openknx_namespace = "openknx");
~GatewayKnxTpIpRouter();
void setConfig(const GatewayKnxConfig& config);
void setCommissioningOnly(bool enabled);
void setGroupWriteHandler(GroupWriteHandler handler);
const GatewayKnxConfig& config() const;
bool tpUartOnline() const;
esp_err_t start(uint32_t task_stack_size, UBaseType_t task_priority);
esp_err_t stop();
@@ -201,17 +216,40 @@ class GatewayKnxTpIpRouter {
bool publishDaliStatus(const GatewayKnxDaliTarget& target, uint8_t actual_level);
private:
static constexpr size_t kMaxTunnelClients = 16;
struct TunnelClient {
bool connected{false};
uint8_t channel_id{0};
uint8_t connection_type{0};
uint8_t received_sequence{255};
uint8_t send_sequence{0};
uint16_t individual_address{0};
TickType_t last_activity_tick{0};
::sockaddr_in control_remote{};
::sockaddr_in data_remote{};
};
static void TaskEntry(void* arg);
esp_err_t initializeRuntime();
void taskLoop();
void finishTask();
void closeSockets();
bool configureSocket();
bool configureTpUart();
bool initializeTpUart();
bool configureProgrammingGpio();
void refreshNetworkInterfaces(bool force_log = false);
void handleUdpDatagram(const uint8_t* data, size_t len, const ::sockaddr_in& remote);
void handleSearchRequest(uint16_t service, const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void handleDescriptionRequest(const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void handleRoutingIndication(const uint8_t* body, size_t len);
void handleTunnellingRequest(const uint8_t* body, size_t len, const ::sockaddr_in& remote);
void handleDeviceConfigurationRequest(const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void handleConnectRequest(const uint8_t* body, size_t len, const ::sockaddr_in& remote);
void handleConnectionStateRequest(const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
@@ -220,47 +258,85 @@ class GatewayKnxTpIpRouter {
const ::sockaddr_in& remote);
void sendTunnellingAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
const ::sockaddr_in& remote);
void sendDeviceConfigurationAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
const ::sockaddr_in& remote);
void sendConnectionHeaderAck(uint16_t service, uint8_t channel_id, uint8_t sequence,
uint8_t status, const ::sockaddr_in& remote);
void sendSecureSessionStatus(uint8_t status, const ::sockaddr_in& remote);
void sendTunnelIndication(const uint8_t* data, size_t len);
void sendTunnelIndicationToClient(TunnelClient& client, const uint8_t* data, size_t len);
void sendConnectionStateResponse(uint8_t channel_id, uint8_t status,
const ::sockaddr_in& remote);
void sendDisconnectResponse(uint8_t channel_id, uint8_t status,
const ::sockaddr_in& remote);
void sendConnectResponse(uint8_t channel_id, uint8_t status,
const ::sockaddr_in& remote);
const ::sockaddr_in& remote, uint8_t connection_type,
uint16_t tunnel_address);
void sendRoutingIndication(const uint8_t* data, size_t len);
bool handleOpenKnxTunnelFrame(const uint8_t* data, size_t len);
void sendSearchResponse(uint16_t service, const ::sockaddr_in& remote,
const std::set<uint8_t>& requested_dibs = {});
void sendDescriptionResponse(const ::sockaddr_in& remote);
std::array<uint8_t, 8> localHpaiForRemote(const ::sockaddr_in& remote) const;
std::vector<uint8_t> buildDeviceInfoDib(const ::sockaddr_in& remote) const;
std::vector<uint8_t> buildSupportedServiceDib() const;
std::vector<uint8_t> buildExtendedDeviceInfoDib() const;
std::vector<uint8_t> buildIpConfigDib(const ::sockaddr_in& remote, bool current) const;
std::vector<uint8_t> buildKnxAddressesDib() const;
std::vector<uint8_t> buildTunnelingInfoDib() const;
TunnelClient* findTunnelClient(uint8_t channel_id);
const TunnelClient* findTunnelClient(uint8_t channel_id) const;
TunnelClient* allocateTunnelClient(const ::sockaddr_in& control_remote,
const ::sockaddr_in& data_remote,
uint8_t connection_type);
void resetTunnelClient(TunnelClient& client);
uint8_t nextTunnelChannelId() const;
uint16_t effectiveTunnelAddressForSlot(size_t slot) const;
void pruneStaleTunnelClients();
bool handleOpenKnxTunnelFrame(const uint8_t* data, size_t len,
TunnelClient* response_client);
bool handleOpenKnxBusFrame(const uint8_t* data, size_t len);
void selectOpenKnxNetworkInterface(const ::sockaddr_in& remote);
bool emitOpenKnxGroupValue(uint16_t group_object_number, const uint8_t* data, size_t len);
bool shouldRouteDaliApplicationFrames() const;
void syncOpenKnxConfigFromDevice();
uint16_t effectiveIndividualAddress() const;
uint16_t effectiveTunnelAddress() const;
void pollTpUart();
void pollProgrammingButton();
void updateProgrammingLed();
void setProgrammingLed(bool on);
void handleTpUartControlByte(uint8_t byte);
void handleTpTelegram(const uint8_t* data, size_t len);
void forwardCemiToTp(const uint8_t* data, size_t len);
GatewayKnxBridge& bridge_;
CemiFrameHandler handler_;
GroupWriteHandler group_write_handler_;
std::string openknx_namespace_;
GatewayKnxConfig config_;
std::unique_ptr<openknx::EtsDeviceRuntime> ets_device_;
TaskHandle_t task_handle_{nullptr};
SemaphoreHandle_t openknx_lock_{nullptr};
SemaphoreHandle_t startup_semaphore_{nullptr};
esp_err_t startup_result_{ESP_OK};
std::atomic_bool stop_requested_{false};
std::atomic_bool started_{false};
int udp_sock_{-1};
int tp_uart_port_{-1};
uint8_t tunnel_channel_id_{1};
uint8_t expected_tunnel_sequence_{0};
uint8_t tunnel_send_sequence_{0};
bool tunnel_connected_{false};
::sockaddr_in tunnel_remote_{};
std::vector<uint32_t> multicast_joined_interfaces_;
TickType_t network_refresh_tick_{0};
std::array<TunnelClient, kMaxTunnelClients> tunnel_clients_{};
uint8_t last_tunnel_channel_id_{0};
std::vector<uint8_t> tp_rx_frame_;
std::vector<uint8_t> tp_last_sent_telegram_;
TickType_t tp_uart_last_byte_tick_{0};
bool tp_uart_extended_frame_{false};
bool tp_uart_online_{false};
bool commissioning_only_{false};
std::atomic_bool openknx_configured_{false};
bool programming_button_last_pressed_{false};
bool programming_led_state_{false};
TickType_t programming_button_last_toggle_tick_{0};
std::string last_error_;
};