Implement KNX Gateway functionality with support for DALI integration
- Added gateway_knx.cpp to handle KNX communication and DALI bridge requests. - Implemented functions for encoding/decoding KNX telegrams and managing group writes. - Introduced GatewayKnxBridge and GatewayKnxTpIpRouter classes for managing KNX to DALI routing and IP tunneling. - Added configuration handling for KNX settings, including UART and multicast options. - Implemented error handling and logging for various KNX operations. Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include "bridge.hpp"
|
||||
#include "model_value.hpp"
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace gateway {
|
||||
|
||||
constexpr uint16_t kGatewayKnxDefaultUdpPort = 3671;
|
||||
constexpr const char* kGatewayKnxDefaultMulticastAddress = "224.0.23.12";
|
||||
constexpr uint32_t kGatewayKnxDefaultTpBaudrate = 19200;
|
||||
|
||||
struct GatewayKnxTpUartConfig {
|
||||
int uart_port{1};
|
||||
int tx_pin{-1};
|
||||
int rx_pin{-1};
|
||||
uint32_t baudrate{kGatewayKnxDefaultTpBaudrate};
|
||||
size_t rx_buffer_size{1024};
|
||||
size_t tx_buffer_size{1024};
|
||||
uint32_t read_timeout_ms{20};
|
||||
};
|
||||
|
||||
struct GatewayKnxConfig {
|
||||
bool dali_router_enabled{true};
|
||||
bool ip_router_enabled{false};
|
||||
bool tunnel_enabled{true};
|
||||
bool multicast_enabled{true};
|
||||
uint8_t main_group{0};
|
||||
uint16_t udp_port{kGatewayKnxDefaultUdpPort};
|
||||
std::string multicast_address{kGatewayKnxDefaultMulticastAddress};
|
||||
uint16_t individual_address{0x1101};
|
||||
GatewayKnxTpUartConfig tp_uart;
|
||||
};
|
||||
|
||||
enum class GatewayKnxDaliDataType : uint8_t {
|
||||
kUnknown = 0,
|
||||
kSwitch = 1,
|
||||
kBrightness = 2,
|
||||
kColorTemperature = 3,
|
||||
kRgb = 4,
|
||||
};
|
||||
|
||||
enum class GatewayKnxDaliTargetKind : uint8_t {
|
||||
kNone = 0,
|
||||
kBroadcast = 1,
|
||||
kShortAddress = 2,
|
||||
kGroup = 3,
|
||||
};
|
||||
|
||||
struct GatewayKnxDaliTarget {
|
||||
GatewayKnxDaliTargetKind kind{GatewayKnxDaliTargetKind::kNone};
|
||||
int address{-1};
|
||||
};
|
||||
|
||||
struct GatewayKnxDaliBinding {
|
||||
uint16_t group_address{0};
|
||||
uint8_t main_group{0};
|
||||
uint8_t middle_group{0};
|
||||
uint8_t sub_group{0};
|
||||
std::string address;
|
||||
std::string name;
|
||||
std::string datapoint_type;
|
||||
GatewayKnxDaliDataType data_type{GatewayKnxDaliDataType::kUnknown};
|
||||
GatewayKnxDaliTarget target;
|
||||
};
|
||||
|
||||
std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value);
|
||||
DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config);
|
||||
|
||||
const char* GatewayKnxDataTypeToString(GatewayKnxDaliDataType data_type);
|
||||
const char* GatewayKnxTargetKindToString(GatewayKnxDaliTargetKind kind);
|
||||
std::optional<GatewayKnxDaliDataType> GatewayKnxDaliDataTypeForMiddleGroup(
|
||||
uint8_t middle_group);
|
||||
std::optional<GatewayKnxDaliTarget> GatewayKnxDaliTargetForSubgroup(uint8_t sub_group);
|
||||
uint16_t GatewayKnxGroupAddress(uint8_t main_group, uint8_t middle_group,
|
||||
uint8_t sub_group);
|
||||
std::string GatewayKnxGroupAddressString(uint16_t group_address);
|
||||
|
||||
class GatewayKnxBridge {
|
||||
public:
|
||||
explicit GatewayKnxBridge(DaliBridgeEngine& engine);
|
||||
|
||||
void setConfig(const GatewayKnxConfig& config);
|
||||
const GatewayKnxConfig& config() const;
|
||||
|
||||
std::vector<GatewayKnxDaliBinding> describeDaliBindings() const;
|
||||
DaliBridgeResult handleCemiFrame(const uint8_t* data, size_t len);
|
||||
DaliBridgeResult handleGroupWrite(uint16_t group_address, const uint8_t* data,
|
||||
size_t len);
|
||||
|
||||
private:
|
||||
DaliBridgeResult executeForDecodedWrite(uint16_t group_address,
|
||||
GatewayKnxDaliDataType data_type,
|
||||
GatewayKnxDaliTarget target,
|
||||
const uint8_t* data, size_t len);
|
||||
|
||||
DaliBridgeEngine& engine_;
|
||||
GatewayKnxConfig config_;
|
||||
};
|
||||
|
||||
class GatewayKnxTpIpRouter {
|
||||
public:
|
||||
using CemiFrameHandler = std::function<DaliBridgeResult(const uint8_t* data, size_t len)>;
|
||||
|
||||
GatewayKnxTpIpRouter(GatewayKnxBridge& bridge, CemiFrameHandler handler);
|
||||
~GatewayKnxTpIpRouter();
|
||||
|
||||
void setConfig(const GatewayKnxConfig& config);
|
||||
const GatewayKnxConfig& config() const;
|
||||
|
||||
esp_err_t start(uint32_t task_stack_size, UBaseType_t task_priority);
|
||||
esp_err_t stop();
|
||||
bool started() const;
|
||||
const std::string& lastError() const;
|
||||
|
||||
private:
|
||||
static void TaskEntry(void* arg);
|
||||
|
||||
void taskLoop();
|
||||
void finishTask();
|
||||
void closeSockets();
|
||||
bool configureSocket();
|
||||
bool configureTpUart();
|
||||
void handleUdpDatagram(const uint8_t* data, 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 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);
|
||||
void handleDisconnectRequest(const uint8_t* body, size_t len, const ::sockaddr_in& remote);
|
||||
void sendTunnellingAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
|
||||
const ::sockaddr_in& remote);
|
||||
void sendTunnelIndication(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);
|
||||
void sendRoutingIndication(const uint8_t* data, size_t len);
|
||||
void pollTpUart();
|
||||
void handleTpTelegram(const uint8_t* data, size_t len);
|
||||
void forwardCemiToTp(const uint8_t* data, size_t len);
|
||||
|
||||
GatewayKnxBridge& bridge_;
|
||||
CemiFrameHandler handler_;
|
||||
GatewayKnxConfig config_;
|
||||
TaskHandle_t task_handle_{nullptr};
|
||||
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<uint8_t> tp_rx_frame_;
|
||||
std::string last_error_;
|
||||
};
|
||||
|
||||
} // namespace gateway
|
||||
Reference in New Issue
Block a user