Refactor KNX DALI Gateway Configuration and Update Device Parameters

- Introduced configuration macros for OEM manufacturer ID, application number, and application version in knxprod.h.
- Updated product identity definitions to use the new configuration macros.
- Modified device type enumeration to include additional device types.
- Adjusted color space enumeration values for consistency.
- Defined generated group object layout constants for memory offsets and block sizes.
- Enhanced knx_dali_gw.cpp to utilize the new configuration macros for manufacturer ID and program version.
- Updated the device initialization logic to reflect the new hardware and program version structures.
- Removed obsolete knx_dali_gw subproject and updated related submodules.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-16 01:51:01 +08:00
parent 2a3808c1e4
commit e79223c87e
18 changed files with 699 additions and 780 deletions
-4
View File
@@ -5,10 +5,6 @@
path = knx
url = https://git.tonycloud.org/knx/knx.git
branch = v1
[submodule "knx_dali_gw"]
path = knx_dali_gw
url = https://git.tonycloud.org/knx/GW-REG1-Dali.git
branch = tonycloud-dev
[submodule "tpuart"]
path = tpuart
url = https://git.tonycloud.org/knx/tpuart.git
+41
View File
@@ -663,6 +663,47 @@ config GATEWAY_KNX_SECURITY_PLAIN_NVS
during bring-up, but production builds should replace it with encrypted
NVS, flash encryption, and secure boot before exposing real keys.
config GATEWAY_KNX_OEM_MANUFACTURER_ID
hex "KNX OEM manufacturer ID"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range 0x0000 0xffff
default 0x00A4
help
Manufacturer ID advertised by the ETS-programmable KNX-DALI gateway
application. This value must match the manufacturer ID used by the
Kaenx Creator generated KNX product database.
config GATEWAY_KNX_OEM_HARDWARE_ID
hex "KNX OEM hardware ID"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range 0x0000 0xffff
default 0xA401
help
Hardware ID encoded into the OpenKNX Device Object hardware type as
0000HHHHVV00. This must match the hardware identifier from the KNX
product database, for example Hardware Id or SerialNumber 0xA401 in
the generated Hardware.xml.
config GATEWAY_KNX_OEM_APPLICATION_NUMBER
hex "KNX OEM application number"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range 0x0000 0xffff
default 0x0001
help
Application number advertised by the ETS-programmable KNX-DALI gateway
application. Keep this in sync with MAIN_ApplicationNumber from the
generated knxprod.h.
config GATEWAY_KNX_OEM_APPLICATION_VERSION
hex "KNX OEM application version"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range 0x00 0xff
default 0x08
help
Application version advertised by the ETS-programmable KNX-DALI gateway
application. Keep this in sync with MAIN_ApplicationVersion from the
generated knxprod.h.
config GATEWAY_KNX_MAIN_GROUP
int "KNX DALI main group"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
+4
View File
@@ -676,6 +676,10 @@ CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED=y
# CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED is not set
# CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS is not set
CONFIG_GATEWAY_KNX_SECURITY_PLAIN_NVS=y
CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID=0x01e5
CONFIG_GATEWAY_KNX_OEM_HARDWARE_ID=0xa401
CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER=0x0001
CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION=0x08
CONFIG_GATEWAY_KNX_MAIN_GROUP=0
CONFIG_GATEWAY_KNX_TUNNEL_ENABLED=y
CONFIG_GATEWAY_KNX_MULTICAST_ENABLED=y
+6 -6
View File
@@ -622,11 +622,8 @@ CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE=1200
#
CONFIG_GATEWAY_CACHE_SUPPORTED=y
CONFIG_GATEWAY_CACHE_START_ENABLED=y
CONFIG_GATEWAY_CACHE_RECONCILIATION_ENABLED=y
# CONFIG_GATEWAY_CACHE_FULL_STATE_MIRROR is not set
# CONFIG_GATEWAY_CACHE_RECONCILIATION_ENABLED is not set
CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS=60000
CONFIG_GATEWAY_CACHE_OUTSIDE_BUS_FIRST=y
# CONFIG_GATEWAY_CACHE_LOCAL_GATEWAY_FIRST is not set
# end of Gateway Cache
# CONFIG_GATEWAY_ENABLE_DALI_BUS is not set
@@ -679,6 +676,9 @@ CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED=y
# CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED is not set
# CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS is not set
CONFIG_GATEWAY_KNX_SECURITY_PLAIN_NVS=y
CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID=0x00fa
CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER=0xa401
CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION=0x08
CONFIG_GATEWAY_KNX_MAIN_GROUP=0
CONFIG_GATEWAY_KNX_TUNNEL_ENABLED=y
CONFIG_GATEWAY_KNX_MULTICAST_ENABLED=y
@@ -1133,8 +1133,8 @@ CONFIG_BT_CTRL_BLE_ADV=y
# Common Options
#
CONFIG_BT_ALARM_MAX_NUM=50
# CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT is not set
CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS=y
CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT=y
# CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS is not set
#
# BLE Log
@@ -72,7 +72,6 @@ class GatewayBridgeService {
esp_err_t startKnxEndpoint(ChannelRuntime* requested_runtime,
std::set<int>* used_uarts = nullptr);
esp_err_t stopKnxEndpoint(ChannelRuntime* requested_runtime);
DaliBridgeResult routeKnxCemiFrame(const uint8_t* data, size_t len);
DaliBridgeResult routeKnxGroupWrite(uint16_t group_address, const uint8_t* data,
size_t len);
void handleDaliRawFrame(const DaliRawFrame& frame);
@@ -1433,11 +1433,7 @@ struct GatewayBridgeService::ChannelRuntime {
}
knx = std::make_unique<GatewayKnxBridge>(*engine);
knx_router = std::make_unique<GatewayKnxTpIpRouter>(
*knx, [this](const uint8_t* data, size_t len) {
return service.routeKnxCemiFrame(data, len);
},
openKnxNamespace());
knx_router = std::make_unique<GatewayKnxTpIpRouter>(*knx, openKnxNamespace());
knx_router->setGroupWriteHandler(
[this](uint16_t group_address, const uint8_t* data, size_t len) {
return service.routeKnxGroupWrite(group_address, data, len);
@@ -4028,36 +4024,6 @@ esp_err_t GatewayBridgeService::stopKnxEndpoint(ChannelRuntime* requested_runtim
return owner->stopKnx();
}
DaliBridgeResult GatewayBridgeService::routeKnxCemiFrame(const uint8_t* data, size_t len) {
std::vector<ChannelRuntime*> matches;
for (const auto& runtime : runtimes_) {
LockGuard guard(runtime->lock);
if (runtime->knx != nullptr && runtime->knx->matchesCemiFrame(data, len)) {
matches.push_back(runtime.get());
}
}
if (matches.empty()) {
DaliBridgeResult result;
result.error = "No DALI bridge mapping matched KNX cEMI group write";
return result;
}
if (matches.size() > 1) {
DaliBridgeResult result;
result.error = "KNX cEMI group write matched multiple DALI bridge channels";
ESP_LOGW(kTag, "%s", result.error.c_str());
return result;
}
ChannelRuntime* runtime = matches.front();
LockGuard guard(runtime->lock);
if (runtime->knx == nullptr || !runtime->knx->matchesCemiFrame(data, len)) {
DaliBridgeResult result;
result.error = "DALI bridge mapping changed before KNX cEMI dispatch";
return result;
}
return runtime->knx->handleCemiFrame(data, len);
}
DaliBridgeResult GatewayBridgeService::routeKnxGroupWrite(uint16_t group_address,
const uint8_t* data, size_t len) {
std::vector<ChannelRuntime*> matches;
@@ -1,5 +1,7 @@
#include "security_storage.h"
#include "gateway_knx_internal.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "esp_timer.h"
@@ -9,7 +11,9 @@
#include <algorithm>
#include <array>
#include <cinttypes>
#include <cstddef>
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <string>
@@ -22,11 +26,7 @@ constexpr const char* kFactoryFdskKey = "factory_fdsk";
constexpr size_t kFdskSize = 16;
constexpr size_t kSerialSize = 6;
constexpr size_t kFdskQrSize = 36;
constexpr uint16_t kKnxManufacturerId = 0x00A4;
constexpr const char* kProductIdentity = "REG1-Dali";
constexpr const char* kManufacturerId = "00A4";
constexpr const char* kApplicationNumber = "01";
constexpr const char* kApplicationVersion = "05";
constexpr const char* kDevelopmentStorage = "base_mac_derived_plain_nvs_development";
constexpr char kFdskDerivationLabel[] = "DaliMaster REG1-Dali deterministic FDSK v1";
constexpr uint8_t kCrc4Tab[16] = {
@@ -38,6 +38,12 @@ constexpr char kHexAlphabet[] = "0123456789ABCDEF";
extern "C" void knx_platform_clear_cached_fdsk() __attribute__((weak));
std::string hexValue(uint32_t value, int width) {
std::array<char, 9> buffer{};
std::snprintf(buffer.data(), buffer.size(), "%0*" PRIX32, width, value);
return buffer.data();
}
bool ensureNvsReady() {
const esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
@@ -123,8 +129,10 @@ bool loadKnxSerialNumber(uint8_t* serial) {
return false;
}
serial[0] = static_cast<uint8_t>((kKnxManufacturerId >> 8) & 0xff);
serial[1] = static_cast<uint8_t>(kKnxManufacturerId & 0xff);
serial[0] = static_cast<uint8_t>(
(gateway::knx_internal::kReg1DaliManufacturerId >> 8) & 0xff);
serial[1] = static_cast<uint8_t>(
gateway::knx_internal::kReg1DaliManufacturerId & 0xff);
std::copy(mac.begin() + 2, mac.end(), serial + 2);
return true;
}
@@ -352,9 +360,11 @@ FactoryCertificatePayload BuildFactoryCertificatePayload() {
}
payload.available = true;
payload.productIdentity = kProductIdentity;
payload.manufacturerId = kManufacturerId;
payload.applicationNumber = kApplicationNumber;
payload.applicationVersion = kApplicationVersion;
payload.manufacturerId = hexValue(gateway::knx_internal::kReg1DaliManufacturerId, 4);
payload.applicationNumber = hexValue(
gateway::knx_internal::kReg1DaliApplicationNumber, 2);
payload.applicationVersion = hexValue(
gateway::knx_internal::kReg1DaliApplicationVersion, 2);
payload.serialNumber = info.serialNumber;
payload.fdskLabel = info.label;
payload.fdskQrCode = info.qrCode;
@@ -9,11 +9,14 @@
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <vector>
namespace gateway::openknx {
class TpuartUartInterface;
class EtsDeviceRuntime {
public:
using CemiFrameSender = std::function<void(const uint8_t* data, size_t len)>;
@@ -25,7 +28,8 @@ class EtsDeviceRuntime {
EtsDeviceRuntime(std::string nvs_namespace,
uint16_t fallback_individual_address,
uint16_t tunnel_client_address = 0);
uint16_t tunnel_client_address = 0,
std::unique_ptr<TpuartUartInterface> tp_uart_interface = nullptr);
~EtsDeviceRuntime();
uint16_t individualAddress() const;
@@ -43,7 +47,12 @@ class EtsDeviceRuntime {
void setFunctionPropertyHandlers(FunctionPropertyHandler command_handler,
FunctionPropertyHandler state_handler);
void setGroupWriteHandler(GroupWriteHandler handler);
void setBusFrameSender(CemiFrameSender sender);
void setNetworkInterface(esp_netif_t* netif);
bool hasTpUart() const;
bool enableTpUart(bool enabled = true);
bool tpUartOnline() const;
bool transmitTpFrame(const uint8_t* data, size_t len);
bool handleTunnelFrame(const uint8_t* data, size_t len, CemiFrameSender sender);
bool handleBusFrame(const uint8_t* data, size_t len);
@@ -70,9 +79,11 @@ class EtsDeviceRuntime {
bool shouldConsumeBusFrame(CemiFrame& frame) const;
std::string nvs_namespace_;
std::unique_ptr<TpuartUartInterface> tp_uart_interface_;
EspIdfPlatform platform_;
Bau07B0 device_;
CemiFrameSender sender_;
CemiFrameSender bus_frame_sender_;
GroupWriteHandler group_write_handler_;
FunctionPropertyHandler command_handler_;
FunctionPropertyHandler state_handler_;
+17 -23
View File
@@ -27,6 +27,7 @@ namespace gateway {
namespace openknx {
class EtsDeviceRuntime;
class TpuartUartInterface;
}
constexpr uint16_t kGatewayKnxDefaultUdpPort = 3671;
@@ -143,9 +144,7 @@ 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);
bool handleFunctionPropertyCommand(uint8_t object_index, uint8_t property_id,
@@ -198,12 +197,11 @@ 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,
GatewayKnxTpIpRouter(GatewayKnxBridge& bridge,
std::string openknx_namespace = "openknx");
~GatewayKnxTpIpRouter();
@@ -256,22 +254,23 @@ class GatewayKnxTpIpRouter {
void handleTcpAccept();
void handleTcpClient(TcpClient& client);
void closeTcpClient(TcpClient& client);
std::unique_ptr<openknx::TpuartUartInterface> createOpenKnxTpUartInterface();
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(const uint8_t* data, size_t len, const ::sockaddr_in& remote);
void handleDescriptionRequest(const uint8_t* data, size_t len,
void handleSearchRequest(uint16_t service, const uint8_t* packet_data, size_t len,
const ::sockaddr_in& remote);
void handleDescriptionRequest(const uint8_t* packet_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 handleDeviceConfigurationRequest(const uint8_t* body, size_t len,
void handleRoutingIndication(const uint8_t* packet_data, size_t len);
void handleTunnellingRequest(const uint8_t* packet_data, size_t len, const ::sockaddr_in& remote);
void handleDeviceConfigurationRequest(const uint8_t* packet_data, 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,
void handleConnectRequest(const uint8_t* packet_data, size_t len, const ::sockaddr_in& remote);
void handleConnectionStateRequest(const uint8_t* packet_data, size_t len,
const ::sockaddr_in& remote);
void handleDisconnectRequest(const uint8_t* body, size_t len, const ::sockaddr_in& remote);
void handleDisconnectRequest(const uint8_t* packet_data, size_t len, const ::sockaddr_in& remote);
void handleSecureService(uint16_t service, const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void sendTunnellingAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
@@ -283,6 +282,8 @@ class GatewayKnxTpIpRouter {
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 sendCemiFrameToClient(TunnelClient& client, uint16_t service,
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,
@@ -319,9 +320,11 @@ class GatewayKnxTpIpRouter {
uint16_t effectiveTunnelAddressForSlot(size_t slot) const;
void pruneStaleTunnelClients();
bool handleOpenKnxTunnelFrame(const uint8_t* data, size_t len,
TunnelClient* response_client);
TunnelClient* response_client, uint16_t response_service);
bool handleOpenKnxBusFrame(const uint8_t* data, size_t len);
bool transmitOpenKnxTpFrame(const uint8_t* data, size_t len);
void selectOpenKnxNetworkInterface(const ::sockaddr_in& remote);
bool routeOpenKnxGroupWrite(const uint8_t* data, size_t len, const char* context);
bool emitOpenKnxGroupValue(uint16_t group_object_number, const uint8_t* data, size_t len);
bool shouldRouteDaliApplicationFrames() const;
uint8_t advertisedMedium() const;
@@ -329,16 +332,11 @@ class GatewayKnxTpIpRouter {
uint16_t effectiveIpInterfaceIndividualAddress() const;
uint16_t effectiveKnxDeviceIndividualAddress() 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_;
@@ -361,10 +359,6 @@ class GatewayKnxTpIpRouter {
std::array<TunnelClient, kMaxTunnelClients> tunnel_clients_{};
std::unique_ptr<IpParameterObject> knx_ip_parameters_;
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};
@@ -1,10 +1,11 @@
#pragma once
// Internal header shared between gateway_knx.cpp and gateway_knx_router.cpp.
// Internal helpers and product identity shared by gateway_knx component sources.
#include "driver/uart.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#include "soc/uart_periph.h"
#include <cstdint>
@@ -15,6 +16,46 @@ namespace knx_internal {
constexpr const char* kTag = "gateway_knx";
#ifndef CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID
#define CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID 0x00A4
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER 0x0001
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION 0x08
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_HARDWARE_ID
#define CONFIG_GATEWAY_KNX_OEM_HARDWARE_ID 0xA401
#endif
inline constexpr uint16_t kReg1DaliManufacturerId =
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID);
inline constexpr uint16_t kReg1DaliHardwareId =
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OEM_HARDWARE_ID);
inline constexpr uint16_t kReg1DaliApplicationNumber =
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER);
inline constexpr uint8_t kReg1DaliApplicationVersion =
static_cast<uint8_t>(CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION);
inline constexpr uint8_t kReg1DaliHardwareType[6] = {
0x00,
0x00,
static_cast<uint8_t>((kReg1DaliHardwareId >> 8) & 0xff),
static_cast<uint8_t>(kReg1DaliHardwareId & 0xff),
kReg1DaliApplicationVersion,
0x00};
inline constexpr uint8_t kReg1DaliOrderNumber[10] = {
'R', 'E', 'G', '1', '-', 'D', 'a', 'l', 'i', 0};
inline constexpr uint8_t kReg1DaliProgramVersion[5] = {
static_cast<uint8_t>((kReg1DaliManufacturerId >> 8) & 0xff),
static_cast<uint8_t>(kReg1DaliManufacturerId & 0xff),
static_cast<uint8_t>((kReg1DaliApplicationNumber >> 8) & 0xff),
static_cast<uint8_t>(kReg1DaliApplicationNumber & 0xff),
kReg1DaliApplicationVersion};
// RAII semaphore guard.
class SemaphoreGuard {
public:
+117 -22
View File
@@ -1,11 +1,17 @@
#include "ets_device_runtime.h"
#include "gateway_knx_internal.h"
#include "esp_log.h"
#include "knx/cemi_server.h"
#include "knx/secure_application_layer.h"
#include "knx/property.h"
#include "tpuart_uart_interface.h"
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <string>
#include <utility>
#include <vector>
@@ -29,53 +35,97 @@ class ActiveFunctionPropertyRuntimeScope {
constexpr uint16_t kInvalidIndividualAddress = 0xffff;
constexpr uint16_t kKnxUnconfiguredBroadcastAddress = 0xffff; // KNX broadcast IA for unconfigured devices
constexpr uint16_t kReg1DaliManufacturerId = 0x00a4;
constexpr uint8_t kReg1DaliApplicationNumber = 0x01;
constexpr uint8_t kReg1DaliApplicationVersion = 0x05;
constexpr uint8_t kReg1DaliOrderNumber[10] = {'R', 'E', 'G', '1', '-', 'D', 'a', 'l', 'i', 0};
bool IsUsableIndividualAddress(uint16_t address) {
return address != 0 && address != kInvalidIndividualAddress;
}
bool IsErasedMemory(const uint8_t* data, size_t size) {
if (data == nullptr || size == 0) {
return true;
std::string HexBytesString(const uint8_t* data, size_t length) {
if (data == nullptr || length == 0) {
return {};
}
return std::all_of(data, data + size, [](uint8_t value) { return value == 0xff; });
std::string out;
out.reserve(length * 3);
char buffer[4] = {0};
for (size_t index = 0; index < length; ++index) {
std::snprintf(buffer, sizeof(buffer), "%02X", data[index]);
out += buffer;
if (index + 1 < length) {
out.push_back(' ');
}
}
return out;
}
std::string PrintableOrderNumber(const uint8_t* data, size_t length) {
if (data == nullptr || length == 0) {
return {};
}
std::string out;
out.reserve(length);
for (size_t index = 0; index < length; ++index) {
if (data[index] == 0) {
break;
}
out.push_back(static_cast<char>(data[index]));
}
return out;
}
void LogReg1DaliIdentity(const std::string& nvs_namespace, Bau07B0& device) {
uint8_t program_version[5] = {0};
if (auto* property = device.parameters().property(PID_PROG_VERSION); property != nullptr) {
property->read(program_version);
}
const std::string hardware_type =
HexBytesString(device.deviceObject().hardwareType(), LEN_HARDWARE_TYPE);
const std::string program_version_hex =
HexBytesString(program_version, sizeof(program_version));
const std::string order_number =
PrintableOrderNumber(device.deviceObject().orderNumber(),
sizeof(knx_internal::kReg1DaliOrderNumber));
ESP_LOGI("gateway_knx",
"OpenKNX identity namespace=%s manufacturer=0x%04x mask=0x%04x deviceVersion=0x%04x hardwareType=%s progVersion=%s order=%s",
nvs_namespace.c_str(), device.deviceObject().manufacturerId(),
device.deviceObject().maskVersion(), device.deviceObject().version(),
hardware_type.c_str(), program_version_hex.c_str(), order_number.c_str());
}
void ApplyReg1DaliIdentity(Bau07B0& device, EspIdfPlatform& platform) {
device.deviceObject().manufacturerId(kReg1DaliManufacturerId);
device.deviceObject().manufacturerId(knx_internal::kReg1DaliManufacturerId);
device.deviceObject().bauNumber(platform.uniqueSerialNumber());
device.deviceObject().orderNumber(kReg1DaliOrderNumber);
const uint8_t program_version[5] = {0x00, 0xa4, 0x00, kReg1DaliApplicationNumber,
kReg1DaliApplicationVersion};
device.parameters().property(PID_PROG_VERSION)->write(program_version);
device.deviceObject().hardwareType(knx_internal::kReg1DaliHardwareType);
device.deviceObject().orderNumber(knx_internal::kReg1DaliOrderNumber);
device.parameters().property(PID_PROG_VERSION)->write(
knx_internal::kReg1DaliProgramVersion);
}
} // namespace
EtsDeviceRuntime::EtsDeviceRuntime(std::string nvs_namespace,
uint16_t fallback_individual_address,
uint16_t tunnel_client_address)
uint16_t tunnel_client_address,
std::unique_ptr<TpuartUartInterface> tp_uart_interface)
: nvs_namespace_(std::move(nvs_namespace)),
platform_(nullptr, nvs_namespace_.c_str()),
tp_uart_interface_(std::move(tp_uart_interface)),
platform_(tp_uart_interface_.get(), nvs_namespace_.c_str()),
device_(platform_) {
platform_.outboundCemiFrameCallback(&EtsDeviceRuntime::HandleOutboundCemiFrame, this);
ApplyReg1DaliIdentity(device_, platform_);
if (IsUsableIndividualAddress(fallback_individual_address)) {
device_.deviceObject().individualAddress(fallback_individual_address);
}
const uint8_t* memory = platform_.getNonVolatileMemoryStart();
const size_t memory_size = platform_.getNonVolatileMemorySize();
if (!IsErasedMemory(memory, memory_size)) {
device_.readMemory();
}
ESP_LOGI("gateway_knx", "OpenKNX loading memory namespace=%s", nvs_namespace_.c_str());
device_.readMemory();
if (!IsUsableIndividualAddress(device_.deviceObject().individualAddress()) &&
IsUsableIndividualAddress(fallback_individual_address)) {
device_.deviceObject().individualAddress(fallback_individual_address);
}
LogReg1DaliIdentity(nvs_namespace_, device_);
if (auto* server = device_.getCemiServer()) {
server->clientAddress(IsUsableIndividualAddress(tunnel_client_address)
? tunnel_client_address
@@ -92,6 +142,9 @@ EtsDeviceRuntime::EtsDeviceRuntime(std::string nvs_namespace,
}
EtsDeviceRuntime::~EtsDeviceRuntime() {
if (tp_uart_interface_ != nullptr) {
device_.enabled(false);
}
platform_.outboundCemiFrameCallback(nullptr, nullptr);
#ifdef USE_DATASECURE
device_.secureGroupWriteCallback(nullptr, nullptr);
@@ -170,10 +223,43 @@ void EtsDeviceRuntime::setGroupWriteHandler(GroupWriteHandler handler) {
group_write_handler_ = std::move(handler);
}
void EtsDeviceRuntime::setBusFrameSender(CemiFrameSender sender) {
bus_frame_sender_ = std::move(sender);
}
void EtsDeviceRuntime::setNetworkInterface(esp_netif_t* netif) {
platform_.networkInterface(netif);
}
bool EtsDeviceRuntime::hasTpUart() const { return tp_uart_interface_ != nullptr; }
bool EtsDeviceRuntime::enableTpUart(bool enabled) {
if (tp_uart_interface_ == nullptr) {
return false;
}
device_.enabled(enabled);
loop();
return !enabled || device_.enabled();
}
bool EtsDeviceRuntime::tpUartOnline() const {
return tp_uart_interface_ != nullptr && const_cast<Bau07B0&>(device_).enabled();
}
bool EtsDeviceRuntime::transmitTpFrame(const uint8_t* data, size_t len) {
auto* data_link_layer = device_.getDataLinkLayer();
if (tp_uart_interface_ == nullptr || data_link_layer == nullptr || data == nullptr || len < 2 ||
!data_link_layer->enabled()) {
return false;
}
std::vector<uint8_t> frame_data(data, data + len);
CemiFrame frame(frame_data.data(), static_cast<uint16_t>(frame_data.size()));
if (!frame.valid()) {
return false;
}
return data_link_layer->transmitFrame(frame);
}
bool EtsDeviceRuntime::handleTunnelFrame(const uint8_t* data, size_t len,
CemiFrameSender sender) {
auto* server = device_.getCemiServer();
@@ -248,10 +334,16 @@ bool EtsDeviceRuntime::HandleOutboundCemiFrame(CemiFrame& frame, void* context)
void EtsDeviceRuntime::EmitTunnelFrame(CemiFrame& frame, void* context) {
auto* self = static_cast<EtsDeviceRuntime*>(context);
if (self == nullptr || !self->sender_) {
if (self == nullptr) {
return;
}
self->sender_(frame.data(), frame.dataLength());
if (self->sender_) {
self->sender_(frame.data(), frame.dataLength());
return;
}
if (self->bus_frame_sender_) {
self->bus_frame_sender_(frame.data(), frame.dataLength());
}
}
void EtsDeviceRuntime::HandleSecureGroupWrite(uint16_t group_address, const uint8_t* data,
@@ -324,6 +416,9 @@ bool EtsDeviceRuntime::shouldConsumeTunnelFrame(CemiFrame& frame) const {
case M_FuncPropStateRead_req:
return true;
case L_data_req: {
if (tpUartOnline()) {
return true;
}
// In commissioning / programming mode ETS may address the device via its
// individual address, the cEMI-client tunnel address (device+1), or the
// unconfigured broadcast address 0xFFFF. Consume only those; let all
@@ -1,6 +1,7 @@
#include "ets_memory_loader.h"
#include "esp_idf_platform.h"
#include "gateway_knx_internal.h"
#include "knx/bau07B0.h"
#include "knx/property.h"
@@ -28,18 +29,13 @@ bool IsErasedMemory(const uint8_t* data, size_t size) {
return std::all_of(data, data + size, [](uint8_t value) { return value == 0xff; });
}
constexpr uint16_t kReg1DaliManufacturerId = 0x00a4;
constexpr uint8_t kReg1DaliApplicationNumber = 0x01;
constexpr uint8_t kReg1DaliApplicationVersion = 0x05;
constexpr uint8_t kReg1DaliOrderNumber[10] = {'R', 'E', 'G', '1', '-', 'D', 'a', 'l', 'i', 0};
void ApplyReg1DaliIdentity(Bau07B0& device, EspIdfPlatform& platform) {
device.deviceObject().manufacturerId(kReg1DaliManufacturerId);
device.deviceObject().manufacturerId(knx_internal::kReg1DaliManufacturerId);
device.deviceObject().bauNumber(platform.uniqueSerialNumber());
device.deviceObject().orderNumber(kReg1DaliOrderNumber);
const uint8_t program_version[5] = {0x00, 0xa4, 0x00, kReg1DaliApplicationNumber,
kReg1DaliApplicationVersion};
device.parameters().property(PID_PROG_VERSION)->write(program_version);
device.deviceObject().hardwareType(knx_internal::kReg1DaliHardwareType);
device.deviceObject().orderNumber(knx_internal::kReg1DaliOrderNumber);
device.parameters().property(PID_PROG_VERSION)->write(
knx_internal::kReg1DaliProgramVersion);
}
} // namespace
File diff suppressed because it is too large Load Diff
+33 -14
View File
@@ -1,13 +1,27 @@
#pragma once
#include "sdkconfig.h"
#ifndef CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID
#define CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID 0x00A4
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER 0x0001
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION 0x08
#endif
// Minimal stub for knxprod.h — generated KNX product definitions.
// The full file (1796 bytes of parameters, 1439 group objects) will be
// adapted in Phase 3 to use the gateway/knx API directly.
// Product identity
#define MAIN_OpenKnxId 0xA4
#define MAIN_ApplicationNumber 1
#define MAIN_ApplicationVersion 5
#define MAIN_OpenKnxId (CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID & 0xff)
#define MAIN_ApplicationNumber CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER
#define MAIN_ApplicationVersion CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION
#define MAIN_OrderNumber "REG1-Dali"
#define MAIN_ParameterSize 1796
#define MAIN_MaxKoNumber 1439
@@ -17,8 +31,13 @@ enum PT_DeviceType : uint8_t {
PT_deviceType_Deaktiviert = 0,
PT_deviceType_DT0 = 1,
PT_deviceType_DT1 = 2,
PT_deviceType_DT6 = 3,
PT_deviceType_DT8 = 4,
PT_deviceType_DT2 = 3,
PT_deviceType_DT3 = 4,
PT_deviceType_DT4 = 5,
PT_deviceType_DT5 = 6,
PT_deviceType_DT6 = 7,
PT_deviceType_DT7 = 8,
PT_deviceType_DT8 = 9,
};
enum PT_ColorType : uint8_t {
@@ -29,8 +48,8 @@ enum PT_ColorType : uint8_t {
};
enum PT_ColorSpace : uint8_t {
PT_colorSpace_rgb = 0,
PT_colorSpace_xy = 1,
PT_colorSpace_rgb = 1,
PT_colorSpace_xy = 0,
};
// Placeholder macros — will be replaced with direct Bau07B0 access in Phase 3.
@@ -57,10 +76,10 @@ enum PT_ColorSpace : uint8_t {
#define ParamHCL_type(channelIndex) (0)
// Group object offset placeholders
#define ADR_KoOffset 0
#define GRP_KoOffset 0
#define HCL_KoOffset 0
#define ADR_KoBlockSize 0
#define GRP_KoBlockSize 0
#define HCL_KoBlockSize 0
// Generated group object layout constants
#define ADR_KoOffset 12
#define GRP_KoOffset 1164
#define HCL_KoOffset 1436
#define ADR_KoBlockSize 18
#define GRP_KoBlockSize 17
#define HCL_KoBlockSize 1
+39 -4
View File
@@ -2,6 +2,8 @@
#include "knx/bau07B0.h"
#include "sdkconfig.h"
#include "esp_log.h"
namespace gateway {
@@ -11,6 +13,38 @@ namespace {
constexpr const char* kTag = "knx_dali_gw";
#ifndef CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID
#define CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID 0x00A4
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER 0x0001
#endif
#ifndef CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION
#define CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION 0x08
#endif
constexpr uint16_t kKnxOemManufacturerId =
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID);
constexpr uint16_t kKnxOemApplicationNumber =
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OEM_APPLICATION_NUMBER);
constexpr uint8_t kKnxOemApplicationVersion =
static_cast<uint8_t>(CONFIG_GATEWAY_KNX_OEM_APPLICATION_VERSION);
constexpr uint8_t kKnxOemHardwareType[6] = {
0x00,
0x00,
static_cast<uint8_t>(kKnxOemManufacturerId & 0xff),
static_cast<uint8_t>(kKnxOemApplicationNumber & 0xff),
kKnxOemApplicationVersion,
0x00};
constexpr uint8_t kKnxOemProgramVersion[5] = {
static_cast<uint8_t>((kKnxOemManufacturerId >> 8) & 0xff),
static_cast<uint8_t>(kKnxOemManufacturerId & 0xff),
static_cast<uint8_t>((kKnxOemApplicationNumber >> 8) & 0xff),
static_cast<uint8_t>(kKnxOemApplicationNumber & 0xff),
kKnxOemApplicationVersion};
} // namespace
// =============================================================================
@@ -31,12 +65,13 @@ struct KnxDaliGateway::Impl {
bool init() {
if (initialized) return true;
device.deviceObject().manufacturerId(0x00a4);
device.deviceObject().manufacturerId(kKnxOemManufacturerId);
device.deviceObject().bauNumber(platform.uniqueSerialNumber());
const uint8_t order_number[10] = {'R', 'E', 'G', '1', '-', 'D', 'a', 'l', 'i', 0};
device.deviceObject().hardwareType(kKnxOemHardwareType);
const uint8_t order_number[10] = {
'R', 'E', 'G', '1', '-', 'D', 'a', 'l', 'i', 0};
device.deviceObject().orderNumber(order_number);
const uint8_t program_version[5] = {0x00, 0xa4, 0x00, 0x01, 0x05};
device.parameters().property(PID_PROG_VERSION)->write(program_version);
device.parameters().property(PID_PROG_VERSION)->write(kKnxOemProgramVersion);
device.readMemory();
+1 -1
Submodule knx updated: dcf565dc03...23b0cddf24
Submodule knx_dali_gw deleted from 6064d84520
+1 -1
Submodule tpuart updated: f8c01e6a32...d95248f994