Add OpenKNX IDF component with TPUart integration
- Created CMakeLists.txt for the OpenKNX IDF component, ensuring dependencies on OpenKNX and TPUart submodules. - Implemented Arduino compatibility header for basic functions like millis, delay, pinMode, and digitalRead. - Developed EspIdfPlatform class for network interface management and multicast communication. - Added EtsMemoryLoader for loading ETS memory snapshots and managing associations. - Introduced TpuartUartInterface for UART communication with methods for reading, writing, and managing callbacks. - Implemented arduino_compat.cpp for Arduino-like functionality on ESP-IDF. - Created source files for platform and memory loader implementations. - Updated submodules for knx, knx_dali_gw, and tpuart. Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -10,6 +10,7 @@ set(GATEWAY_BRIDGE_REQUIRES
|
||||
log
|
||||
lwip
|
||||
nvs_flash
|
||||
openknx_idf
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "gateway_knx.hpp"
|
||||
#include "gateway_modbus.hpp"
|
||||
#include "gateway_provisioning.hpp"
|
||||
#include "openknx_idf/ets_memory_loader.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "driver/uart.h"
|
||||
@@ -1206,6 +1207,10 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
return "dali_cloud_" + std::to_string(channel.gateway_id);
|
||||
}
|
||||
|
||||
std::string openKnxNamespace() const {
|
||||
return "openknx_" + std::to_string(channel.gateway_id);
|
||||
}
|
||||
|
||||
esp_err_t start() {
|
||||
comm = std::make_unique<DaliComm>(
|
||||
[this](const uint8_t* data, size_t len) {
|
||||
@@ -1254,6 +1259,8 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
engine->upsertModel(model);
|
||||
}
|
||||
|
||||
refreshOpenKnxEtsAssociationsLocked();
|
||||
|
||||
modbus = std::make_unique<GatewayModbusBridge>(*engine);
|
||||
if (modbus_config.has_value()) {
|
||||
modbus->setConfig(modbus_config.value());
|
||||
@@ -1292,6 +1299,31 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
diagnostic_snapshot_cache.clear();
|
||||
}
|
||||
|
||||
void refreshOpenKnxEtsAssociationsLocked() {
|
||||
if (!service_config.knx_enabled) {
|
||||
return;
|
||||
}
|
||||
const auto active_config = activeKnxConfigLocked();
|
||||
if (!active_config.has_value()) {
|
||||
return;
|
||||
}
|
||||
const auto snapshot = openknx::LoadEtsMemorySnapshot(openKnxNamespace());
|
||||
if (snapshot.associations.empty()) {
|
||||
return;
|
||||
}
|
||||
GatewayKnxConfig updated = active_config.value();
|
||||
updated.ets_associations.clear();
|
||||
updated.ets_associations.reserve(snapshot.associations.size());
|
||||
for (const auto& association : snapshot.associations) {
|
||||
updated.ets_associations.push_back(GatewayKnxEtsAssociation{
|
||||
association.group_address, association.group_object_number});
|
||||
}
|
||||
knx_config = std::move(updated);
|
||||
ESP_LOGI(kTag, "gateway=%u loaded %u OpenKNX ETS associations from NVS namespace %s",
|
||||
channel.gateway_id, static_cast<unsigned>(snapshot.associations.size()),
|
||||
openKnxNamespace().c_str());
|
||||
}
|
||||
|
||||
std::optional<DaliDomainSnapshot> diagnosticSnapshotLocked(int short_address,
|
||||
std::string_view kind) {
|
||||
if (!ValidShortAddress(short_address) || kind.empty()) {
|
||||
@@ -2004,13 +2036,15 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
knx_last_error.empty() ? router_error.c_str()
|
||||
: knx_last_error.c_str());
|
||||
if (effective_knx.has_value()) {
|
||||
cJSON_AddBoolToObject(knx_json, "daliRouterEnabled",
|
||||
effective_knx->dali_router_enabled);
|
||||
cJSON_AddBoolToObject(knx_json, "ipRouterEnabled",
|
||||
effective_knx->ip_router_enabled);
|
||||
cJSON_AddBoolToObject(knx_json, "daliRouterEnabled", effective_knx->dali_router_enabled);
|
||||
cJSON_AddBoolToObject(knx_json, "ipRouterEnabled", effective_knx->ip_router_enabled);
|
||||
cJSON_AddBoolToObject(knx_json, "tunnelEnabled", effective_knx->tunnel_enabled);
|
||||
cJSON_AddBoolToObject(knx_json, "multicastEnabled",
|
||||
effective_knx->multicast_enabled);
|
||||
cJSON_AddBoolToObject(knx_json, "multicastEnabled", effective_knx->multicast_enabled);
|
||||
cJSON_AddBoolToObject(knx_json, "etsDatabaseEnabled", effective_knx->ets_database_enabled);
|
||||
cJSON_AddNumberToObject(knx_json, "etsBindingCount",
|
||||
knx == nullptr ? 0 : knx->etsBindingCount());
|
||||
cJSON_AddStringToObject(knx_json, "mappingMode",
|
||||
GatewayKnxMappingModeToString(effective_knx->mapping_mode));
|
||||
cJSON_AddNumberToObject(knx_json, "mainGroup", effective_knx->main_group);
|
||||
cJSON_AddNumberToObject(knx_json, "udpPort", effective_knx->udp_port);
|
||||
cJSON_AddStringToObject(knx_json, "multicastAddress",
|
||||
@@ -2262,6 +2296,17 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
cJSON_AddNumberToObject(item, "mainGroup", binding.main_group);
|
||||
cJSON_AddNumberToObject(item, "middleGroup", binding.middle_group);
|
||||
cJSON_AddNumberToObject(item, "subGroup", binding.sub_group);
|
||||
cJSON_AddStringToObject(item, "mappingMode",
|
||||
GatewayKnxMappingModeToString(binding.mapping_mode));
|
||||
if (binding.group_object_number >= 0) {
|
||||
cJSON_AddNumberToObject(item, "objectNumber", binding.group_object_number);
|
||||
}
|
||||
if (binding.channel_index >= 0) {
|
||||
cJSON_AddNumberToObject(item, "channelIndex", binding.channel_index);
|
||||
}
|
||||
if (!binding.object_role.empty()) {
|
||||
cJSON_AddStringToObject(item, "objectRole", binding.object_role.c_str());
|
||||
}
|
||||
cJSON_AddStringToObject(item, "name", binding.name.c_str());
|
||||
cJSON_AddStringToObject(item, "datapointType", binding.datapoint_type.c_str());
|
||||
cJSON_AddStringToObject(item, "dataType",
|
||||
@@ -2894,22 +2939,28 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
std::set<uint16_t>* used_ports = nullptr,
|
||||
std::set<int>* used_uarts = nullptr) {
|
||||
LockGuard guard(lock);
|
||||
GatewayKnxConfig merged_config = config;
|
||||
const auto previous_knx = activeKnxConfigLocked();
|
||||
if (merged_config.ets_associations.empty() && previous_knx.has_value() &&
|
||||
!previous_knx->ets_associations.empty()) {
|
||||
merged_config.ets_associations = previous_knx->ets_associations;
|
||||
}
|
||||
std::string validation_error;
|
||||
const esp_err_t validation_err = validateKnxConfigLocked(
|
||||
config, activeModbusConfigLocked(), &validation_error);
|
||||
merged_config, activeModbusConfigLocked(), &validation_error);
|
||||
if (validation_err != ESP_OK) {
|
||||
knx_last_error = validation_error;
|
||||
return validation_err;
|
||||
}
|
||||
const bool restart_router = knx_started || (knx_router != nullptr && knx_router->started());
|
||||
if (restart_router && config.ip_router_enabled && used_ports != nullptr &&
|
||||
used_ports->find(config.udp_port) != used_ports->end()) {
|
||||
knx_last_error = "duplicate KNXnet/IP UDP port " + std::to_string(config.udp_port);
|
||||
if (restart_router && merged_config.ip_router_enabled && used_ports != nullptr &&
|
||||
used_ports->find(merged_config.udp_port) != used_ports->end()) {
|
||||
knx_last_error = "duplicate KNXnet/IP UDP port " + std::to_string(merged_config.udp_port);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (restart_router && config.ip_router_enabled && used_uarts != nullptr &&
|
||||
used_uarts->find(config.tp_uart.uart_port) != used_uarts->end()) {
|
||||
knx_last_error = "KNX TP-UART UART" + std::to_string(config.tp_uart.uart_port) +
|
||||
if (restart_router && merged_config.ip_router_enabled && used_uarts != nullptr &&
|
||||
used_uarts->find(merged_config.tp_uart.uart_port) != used_uarts->end()) {
|
||||
knx_last_error = "KNX TP-UART UART" + std::to_string(merged_config.tp_uart.uart_port) +
|
||||
" is already used by another runtime";
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
@@ -2920,18 +2971,18 @@ struct GatewayBridgeService::ChannelRuntime {
|
||||
BridgeProvisioningStore store(bridgeNamespace());
|
||||
const esp_err_t err = store.saveObject(
|
||||
kBridgeConfigKey,
|
||||
GatewayBridgeStoredConfigToValue(bridge_config, modbus_config, config,
|
||||
GatewayBridgeStoredConfigToValue(bridge_config, modbus_config, merged_config,
|
||||
bacnet_server_config));
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
knx_config = config;
|
||||
knx_config = merged_config;
|
||||
bridge_config_loaded = true;
|
||||
if (knx != nullptr) {
|
||||
knx->setConfig(config);
|
||||
knx->setConfig(merged_config);
|
||||
}
|
||||
if (knx_router != nullptr) {
|
||||
knx_router->setConfig(config);
|
||||
knx_router->setConfig(merged_config);
|
||||
}
|
||||
if (restart_router) {
|
||||
return startKnx(used_ports, used_uarts);
|
||||
|
||||
Reference in New Issue
Block a user