Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29920195ba | |||
| 67c2410be7 | |||
| 8bda4f727e | |||
| a46cef3ee9 | |||
| 4c9f26a638 | |||
| 803348ea3a | |||
| 8211514fe3 | |||
| 078c37a20f | |||
| 6d0b36b60a | |||
| 7a820e700c | |||
| 5622e6ba81 | |||
| 865bf8425a | |||
| f922993d2f | |||
| 2b779d5532 | |||
| 0467179f70 | |||
| 20642e5ec3 | |||
| 0827befb06 | |||
| 2b8ef31263 | |||
| 8e80fd05b4 | |||
| 7ab1855295 | |||
| e8bf8c56cd | |||
| 416320604b | |||
| e489a2de36 | |||
| b73e0c6138 | |||
| 3165afe8a4 | |||
| 57950e7b0b | |||
| a3f03719f9 | |||
| e091b4301e | |||
| 226855362b | |||
| 3bc5355041 | |||
| 3af2995b40 | |||
| b447da5bfc | |||
| f2b7dee8bd | |||
| 77fe8c1b02 | |||
| 82142dd46c | |||
| 323ff24c04 | |||
| 277379abd7 | |||
| e79223c87e | |||
| 2a3808c1e4 | |||
| 449a3a801a | |||
| 3f15cd7f3f | |||
| f005d2bc09 | |||
| 70ae1ae6cf | |||
| f2ffb45ca6 |
@@ -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
|
||||
|
||||
@@ -26,22 +26,56 @@ update as the project evolves:
|
||||
- `knx`: The forked OpenKNX cEMI programming support, ESP-IDF port, and KNX security storage used by the gateway. You can edit this code when necessary to support missing ETS programming features or to implement the secure-session transport path.
|
||||
- `knx_dali_gw`: The forked OpenKNX DALI-GW function-property support used by the gateway. You can edit this code when necessary to support missing DALI-GW features or to fix bugs.
|
||||
|
||||
## Gateway DALI cache
|
||||
|
||||
The gateway owns the shared DALI cache for multi-user deployments. App, BLE,
|
||||
IP, Modbus, BACnet, KNX, and local control paths should treat the gateway cache
|
||||
as the shared read surface instead of maintaining separate app-side device-state
|
||||
caches. Transparent/setup raw forwarding paths remain bypass-oriented, but raw
|
||||
DALI bus observation still feeds the passive decoder when frames are visible to
|
||||
the gateway.
|
||||
|
||||
`gateway_cache` stores internal scene/group data and per-short-address DALI
|
||||
state. Device settings, group masks, scene levels, known flags, and the last
|
||||
runtime status are batched to NVS using `GATEWAY_CACHE_FLUSH_INTERVAL_MS`, which
|
||||
defaults to 10000 ms. Runtime status loaded from disk is marked stale until the
|
||||
gateway observes a bus command or the background refresher verifies it again.
|
||||
|
||||
`GATEWAY_CACHE_REFRESH_INTERVAL_MS` defaults to 120000 ms. When nonzero, the
|
||||
controller maintenance loop refreshes direct short-address actual levels one
|
||||
small step at a time. The refresh is low priority: it yields to queued gateway
|
||||
commands, address allocation, live management reads, bridge traffic, and any raw
|
||||
DALI bus activity from another master until the bus has been idle. Group and
|
||||
broadcast targets are never queried for refresh.
|
||||
|
||||
Gateway feature opcode `0x06` advertises cache support with bit `0x40`. Gateway
|
||||
opcode `0x39` returns cache summary and target snapshots so frontend clients can
|
||||
read cached state without issuing live DALI queries on supported gateways.
|
||||
|
||||
## Current status
|
||||
|
||||
The native rewrite now wires a shared `gateway_core` bootstrap component, a multi-channel `dali_domain` wrapper over `dali_cpp`, a local vendored `dali` hardware backend from the LuatOS ESP-IDF port with raw receive fan-out, an initial `gateway_runtime` service that provides persistent settings, device info, Lua-compatible command framing helpers, and Lua-style query command deduplication, plus a `gateway_controller` service that starts the gateway command task, dispatches core Lua gateway opcodes, and owns internal scene/group state. The gateway app also includes a `gateway_ble` NimBLE bridge that advertises a Lua-compatible GATT service and forwards `FFF3` framed notifications, incoming `FFF1`/`FFF2`/`FFF3` writes, and native raw DALI frame notifications into the matching raw channel, a `gateway_network` service that provides the native HTTP `/info`, `GET`/`POST /dali/cmd`, `/led/1`, `/led/0`, `/jq.js`, UDP control-plane router on port `2020`, W5500 SPI Ethernet with DHCP, Wi-Fi STA lifecycle, ESP-Touch smartconfig credential provisioning, the Lua-style `LAMMIN_Gateway` setup AP on `192.168.3.1`, ESP-NOW setup ingress for Lua-compatible `connReq`/`connAck`/`echo`/`cmd`/`data`/`uart` packets, native raw DALI frame forwarding back to connected setup peers, setup AP GPIO entry, and optional Wi-Fi credential reset GPIO handling, and an optional `gateway_485_control` bridge that claims UART0 for Lua-compatible framed command ingress plus `0x22` notification egress when the console is moved off UART0. Startup behavior is configured in `main/Kconfig.projbuild`: BLE and wired Ethernet are enabled by default, W5500 initialization and startup probe failures are ignored by default for boards without populated Ethernet hardware by fully disabling Ethernet for that boot, Wi-Fi STA, smartconfig, and ESP-NOW setup mode are disabled by default, the built-in USB Serial/JTAG interface stays in debug mode unless the optional USB setup bridge mode is selected, and the UART0 control bridge stays disabled unless the deployment explicitly repurposes UART0 away from the ESP-IDF console. Runtime settings and internal scene/group data are cached in RAM after load, skip unchanged flash writes, and batch Wi-Fi credential commits to reduce flash stalls on ESP32-S3 boards where flash and PSRAM share the SPI bus. The gateway app exposes per-channel PHY selection through `main/Kconfig.projbuild`; each channel can be disabled, bound to the native DALI GPIO HAL, or bound to a UART1/UART2 serial PHY. The checked-in `sdkconfig` is aligned with the app's custom 16 MB partition table so the Wi-Fi/BLE/network-enabled image fits the OTA app slots.
|
||||
|
||||
## KNX Security
|
||||
|
||||
KNX Data Secure and KNXnet/IP Secure support are controlled by `GATEWAY_KNX_DATA_SECURE_SUPPORTED` and `GATEWAY_KNX_IP_SECURE_SUPPORTED`. The current KNXnet/IP Secure flag reserves and reports secure service capability, while runtime secure-session transport is still reported as not implemented until that path is wired. The gateway derives its KNX serial identity from the ESP base MAC, and the development factory setup key is deterministically derived from that KNX serial so the same board keeps the same FDSK across NVS erases.
|
||||
KNX Data Secure and KNXnet/IP Secure support are controlled by `GATEWAY_KNX_DATA_SECURE_SUPPORTED` and `GATEWAY_KNX_IP_SECURE_SUPPORTED`. KNXnet/IP Secure now recognizes the secure service family, performs secure session setup/authentication with provisioned tunnel user keys, wraps secure tunnel responses, handles secure group-sync frames, and wraps/unpacks secure multicast routing frames when an OAM backbone key is active. The gateway derives its KNX serial identity from the ESP base MAC, and the development factory setup key is deterministically derived from that KNX serial so the same board keeps the same FDSK across NVS erases.
|
||||
|
||||
The KNXnet/IP tunnel can start from the built-in default configuration before any ETS download. KNX TP-UART is enabled only when `GATEWAY_KNX_TP_UART_PORT` is `0`, `1`, or `2`; set that UART port to `-1` for IP-only operation. UART TX/RX GPIO values of `-1` mean use the ESP-IDF target default pins for that UART, not disabled. `GATEWAY_KNX_TP_UART_9BIT_MODE` enables the NCN5120/OpenKNX-style 9-bit host frame on the wire, represented on ESP-IDF as 8 data bits plus even parity. Non-UART GPIO options use `-1` as disabled, including the KNX programming button, KNX programming LED, setup AP button, Wi-Fi reset button, and status LED.
|
||||
The shared KNXnet/IP endpoint can also be provisioned with an OAM-compatible IP-Router persona by enabling `GATEWAY_KNX_OAM_ROUTER_SUPPORTED` and the nested `knx.oamRouter` config. This second logical application is part of the same KNX/IP router endpoint: it does not open a second UDP/TCP listener and it does not own a second TP-UART driver. When OAM is enabled, KNXnet/IP search, description, tunnel, and multicast identity are advertised as the OAM IP router and follow the OAM individual address instead of a DALI-channel namespace. The gateway hosts a BAU091A/OAM router runtime beside the REG1-Dali BAU07B0 runtime, with separate individual/tunnel addresses, separate programming button/LED GPIOs, separate ETS/security storage, and a KNX serial derived from the ESP base MAC plus one. Secure tunnels are assigned to the OAM persona, and non-secure IP management/tunnel connections opened while OAM programming mode is active are also assigned to the OAM persona so ETS can modify and verify the OAM individual address. OAM-addressed management frames are dispatched to the BAU091A runtime while DALI group/function-property traffic stays on the REG1-Dali application. Physical TP ingress is brokered at the shared TP-UART owner: OAM-addressed TP telegrams and broadcast management telegrams are delivered to the matching local logical runtime, and OAM responses are transmitted through the same TP-UART and mirrored to KNXnet/IP once. Normal REG1-Dali TP group/application traffic remains on the existing REG1 data-link path so ETS group-object dispatch and DALI routing do not duplicate. The default OAM identity follows the OAM-IP-Router release database (`0x00FA` manufacturer, `0xA11F` application number, version `0x07`) unless overridden in Kconfig.
|
||||
|
||||
OAM IP Secure keyring preparation uses the `knx_oam_sec` NVS namespace. Development HTTP actions can read/generate/reset/export the OAM factory setup key and store already-extracted IP Secure keyring material (`backboneKeyHex`, tunnel user keys, and an optional device-authentication key). Stored OAM credentials are reported in `knx.security.oamRouter.ipSecureCredentials`; the tunnel user keys authenticate secure sessions and the backbone key protects secure routing/group-sync traffic. The routing sequence counter is persisted back to NVS after secure routing sends or authenticated sync updates.
|
||||
|
||||
Cloud KNX remote-access preparation is part of the `knx.oamRouter.cloudRemote` config. The status JSON reports the selected mode (`mqtt`, relay, or UDP punch-through-oriented deployments), whether secure tunnels are required, and whether relay endpoint, MQTT topic prefix, and token-reference fields are configured. The firmware does not start an external relay client yet; this config is the stable handoff surface for a future UDP relay/MQTT tunnel transport that will reuse the secure OAM tunnel path.
|
||||
|
||||
The KNXnet/IP tunnel can start from the built-in default configuration before any ETS download. KNX TP-UART is enabled only when `GATEWAY_KNX_TP_UART_PORT` is `0`, `1`, or `2`; set that UART port to `-1` for IP-only operation. UART TX/RX GPIO values of `-1` mean use the ESP-IDF target default pins for that UART, not disabled. `GATEWAY_KNX_TP_UART_9BIT_MODE` enables the NCN5120/OpenKNX-style 9-bit host frame on the wire, represented on ESP-IDF as 8 data bits plus even parity. Enable `GATEWAY_KNX_TP_FULL_IP_FORWARD` when the gateway must mirror all physical TP telegrams back out to KNXnet/IP tunnelling and multicast so ETS can monitor or download other TP devices through the gateway's IP endpoint. Local logical-device responses are de-duplicated by exact cEMI payload at the tunnel egress, so the same response is not replayed through tunnel, routing, and TP echo paths while distinct source addresses remain visible as separate devices. Non-UART GPIO options use `-1` as disabled, including the KNX programming button, KNX programming LED, setup AP button, Wi-Fi reset button, and status LED.
|
||||
|
||||
When no KNX bridge config or ETS application data has been downloaded, the KNXnet/IP router starts in commissioning mode: OpenKNX receives tunnel programming traffic from ETS, while DALI group routing and REG1-Dali function-property actions stay inactive until ETS reports a configured application.
|
||||
|
||||
The bridge service exposes one shared KNXnet/IP endpoint per physical gateway on the configured UDP port. Per-channel DALI/KNX bridge runtimes keep their own group-address mappings behind that endpoint, and incoming group writes are dispatched to the matching channel instead of starting one UDP socket per DALI channel.
|
||||
The bridge service exposes one chip-level KNXnet/IP endpoint per physical gateway module on the configured UDP port. Per-channel DALI/KNX bridge runtimes keep their own group-address mappings behind that endpoint, and incoming group writes are dispatched to matching channel bridges instead of starting one UDP socket or multicast responder per DALI channel. If a non-owner channel router was already running, starting the shared endpoint stops it so KNXnet/IP search and description requests receive one response from the chip-level interface.
|
||||
|
||||
KNX programming mode can be controlled locally with `GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO`, and `GATEWAY_KNX_PROGRAMMING_LED_GPIO` mirrors the current programming-mode state. The setup AP entry button is configured separately with `GATEWAY_SETUP_AP_BUTTON_GPIO`; Wi-Fi credential reset remains a separate long-press function on `GATEWAY_BOOT_BUTTON_GPIO` when enabled.
|
||||
|
||||
When the OAM router persona is enabled, use `GATEWAY_KNX_OAM_PROGRAMMING_BUTTON_GPIO` and `GATEWAY_KNX_OAM_PROGRAMMING_LED_GPIO` for its separate programming controls. The bridge config validator rejects duplicate REG1-Dali and OAM programming button or LED GPIO assignments so ETS programming-mode selection remains unambiguous.
|
||||
|
||||
When `GATEWAY_KNX_SECURITY_DEV_ENDPOINTS` is enabled, the bridge HTTP action surface exposes development-only operations for reading, writing, generating, and resetting the factory setup key, exporting the factory certificate payload, and clearing local KNX security failure diagnostics. These endpoints require explicit confirmation fields in the JSON body and should stay disabled in production builds. The default development storage mode is plain NVS via `GATEWAY_KNX_SECURITY_PLAIN_NVS`; production builds should replace that with encrypted NVS, flash encryption, and secure boot before handling real commissioning keys.
|
||||
|
||||
The normal bridge status response includes a `knx.security` object with compile-time capability flags, storage mode, factory setup key metadata, factory certificate metadata, and security failure counters/log entries. Secret FDSK strings are returned only by the explicit development actions, not by passive status polling.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
CompileFlags:
|
||||
Remove: [-f*, -m*]
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"idf.currentSetup": "/Users/tonylu/.espressif/v5.5.4/esp-idf",
|
||||
"idf.customExtraVars": {
|
||||
"OPENOCD_SCRIPTS": "/Users/tonylu/.espressif/tools/openocd-esp32/v0.12.0-esp32-20240318/openocd-esp32/share/openocd/scripts",
|
||||
"ESP_ROM_ELF_DIR": "/Users/tonylu/.espressif/tools/esp-rom-elfs/20240305/"
|
||||
},
|
||||
"clangd.path": "/Users/tonylu/.espressif/tools/esp-clang/esp-20.1.1_20250829/esp-clang/bin/clangd",
|
||||
"clangd.arguments": [
|
||||
"--background-index",
|
||||
"--query-driver=**",
|
||||
"--compile-commands-dir=/Users/tonylu/StudioProjects/dalimaster/gateway/apps/gateway/build"
|
||||
]
|
||||
}
|
||||
@@ -6,6 +6,7 @@ endif()
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../../components"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../../knx"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../../../dali_cpp"
|
||||
)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+368
-128
@@ -12,6 +12,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -107,6 +108,10 @@
|
||||
#define CONFIG_GATEWAY_485_CONTROL_TASK_PRIORITY 4
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS
|
||||
#define CONFIG_GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS 2000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC
|
||||
#define CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC 60
|
||||
#endif
|
||||
@@ -159,6 +164,10 @@
|
||||
#define CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE 3072
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_CONTROLLER_TASK_STACK_SIZE
|
||||
#define CONFIG_GATEWAY_CONTROLLER_TASK_STACK_SIZE 12288
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_STACK_SIZE
|
||||
#define CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_STACK_SIZE 6144
|
||||
#endif
|
||||
@@ -219,6 +228,10 @@
|
||||
#define CONFIG_GATEWAY_KNX_MAIN_GROUP 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_DALI_BUS_ID
|
||||
#define CONFIG_GATEWAY_KNX_DALI_BUS_ID 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_UDP_PORT
|
||||
#define CONFIG_GATEWAY_KNX_UDP_PORT 3671
|
||||
#endif
|
||||
@@ -235,6 +248,22 @@
|
||||
#define CONFIG_GATEWAY_KNX_IP_INTERFACE_INDIVIDUAL_ADDRESS 65281
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_INDIVIDUAL_ADDRESS
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_INDIVIDUAL_ADDRESS 65282
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_TUNNEL_ADDRESS_BASE
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_TUNNEL_ADDRESS_BASE 65296
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_BUTTON_GPIO
|
||||
#define CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_BUTTON_GPIO -1
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_LED_GPIO
|
||||
#define CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_LED_GPIO -1
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO
|
||||
#define CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO -1
|
||||
#endif
|
||||
@@ -260,7 +289,11 @@
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS
|
||||
#define CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS 5000
|
||||
#define CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS 10000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_CACHE_REFRESH_INTERVAL_MS
|
||||
#define CONFIG_GATEWAY_CACHE_REFRESH_INTERVAL_MS 120000
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
@@ -388,6 +421,18 @@ constexpr bool kKnxMulticastEnabled = true;
|
||||
constexpr bool kKnxMulticastEnabled = false;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GATEWAY_KNX_OAM_ROUTER_SUPPORTED
|
||||
constexpr bool kKnxOamRouterSupported = true;
|
||||
#else
|
||||
constexpr bool kKnxOamRouterSupported = false;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GATEWAY_KNX_OAM_ROUTER_ENABLED
|
||||
constexpr bool kKnxOamRouterEnabled = true;
|
||||
#else
|
||||
constexpr bool kKnxOamRouterEnabled = false;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED
|
||||
constexpr bool kCloudBridgeSupported = true;
|
||||
#else
|
||||
@@ -485,57 +530,245 @@ struct ChannelBindingConfig {
|
||||
bool enabled{false};
|
||||
bool native_phy{false};
|
||||
bool serial_phy{false};
|
||||
uint8_t channel_index{0};
|
||||
uint8_t gateway_id{0};
|
||||
uint8_t native_bus_id{0};
|
||||
uint32_t native_baudrate{0};
|
||||
int native_tx_pin{-1};
|
||||
int native_rx_pin{-1};
|
||||
int uart_port{-1};
|
||||
int serial_tx_pin{-1};
|
||||
int serial_rx_pin{-1};
|
||||
uint32_t serial_baudrate{0};
|
||||
size_t serial_rx_buffer_size{0};
|
||||
size_t serial_tx_buffer_size{0};
|
||||
uint32_t serial_query_timeout_ms{0};
|
||||
};
|
||||
|
||||
ChannelBindingConfig NativeChannelConfig(uint8_t channel_index, uint8_t gateway_id,
|
||||
uint8_t bus_id, int tx_pin, int rx_pin,
|
||||
uint32_t baudrate) {
|
||||
ChannelBindingConfig channel{};
|
||||
channel.enabled = true;
|
||||
channel.native_phy = true;
|
||||
channel.channel_index = channel_index;
|
||||
channel.gateway_id = gateway_id;
|
||||
channel.native_bus_id = bus_id;
|
||||
channel.native_tx_pin = tx_pin;
|
||||
channel.native_rx_pin = rx_pin;
|
||||
channel.native_baudrate = baudrate;
|
||||
return channel;
|
||||
}
|
||||
|
||||
[[maybe_unused]] ChannelBindingConfig SerialChannelConfig(uint8_t channel_index, uint8_t gateway_id,
|
||||
int uart_port, int tx_pin, int rx_pin,
|
||||
uint32_t baudrate, size_t rx_buffer_size,
|
||||
size_t tx_buffer_size,
|
||||
uint32_t query_timeout_ms) {
|
||||
ChannelBindingConfig channel{};
|
||||
channel.enabled = true;
|
||||
channel.serial_phy = true;
|
||||
channel.channel_index = channel_index;
|
||||
channel.gateway_id = gateway_id;
|
||||
channel.uart_port = uart_port;
|
||||
channel.serial_tx_pin = tx_pin;
|
||||
channel.serial_rx_pin = rx_pin;
|
||||
channel.serial_baudrate = baudrate;
|
||||
channel.serial_rx_buffer_size = rx_buffer_size;
|
||||
channel.serial_tx_buffer_size = tx_buffer_size;
|
||||
channel.serial_query_timeout_ms = query_timeout_ms;
|
||||
return channel;
|
||||
}
|
||||
|
||||
#define GATEWAY_CONFIGURE_NATIVE_CHANNEL(slot, number) \
|
||||
channels[slot] = NativeChannelConfig( \
|
||||
static_cast<uint8_t>(slot), static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL##number##_GW_ID), \
|
||||
static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL##number##_NATIVE_BUS_ID), \
|
||||
CONFIG_GATEWAY_CHANNEL##number##_NATIVE_TX_PIN, \
|
||||
CONFIG_GATEWAY_CHANNEL##number##_NATIVE_RX_PIN, \
|
||||
static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL##number##_NATIVE_BAUDRATE))
|
||||
|
||||
#define GATEWAY_CONFIGURE_SERIAL_CHANNEL(slot, number, uart) \
|
||||
channels[slot] = SerialChannelConfig( \
|
||||
static_cast<uint8_t>(slot), static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL##number##_GW_ID), \
|
||||
uart, CONFIG_GATEWAY_CHANNEL##number##_SERIAL_TX_PIN, \
|
||||
CONFIG_GATEWAY_CHANNEL##number##_SERIAL_RX_PIN, \
|
||||
static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL##number##_SERIAL_BAUDRATE), \
|
||||
static_cast<size_t>(CONFIG_GATEWAY_CHANNEL##number##_SERIAL_RX_BUFFER), \
|
||||
static_cast<size_t>(CONFIG_GATEWAY_CHANNEL##number##_SERIAL_TX_BUFFER), \
|
||||
static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL##number##_SERIAL_QUERY_TIMEOUT_MS))
|
||||
|
||||
std::array<ChannelBindingConfig, CONFIG_GATEWAY_CHANNEL_COUNT> BuildChannelBindings() {
|
||||
std::array<ChannelBindingConfig, CONFIG_GATEWAY_CHANNEL_COUNT> channels{};
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(0, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL1_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(0, 1, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL1_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(0, 1, 2);
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 2
|
||||
#if CONFIG_GATEWAY_CHANNEL2_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(1, 2);
|
||||
#elif CONFIG_GATEWAY_CHANNEL2_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(1, 2, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL2_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(1, 2, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 3
|
||||
#if CONFIG_GATEWAY_CHANNEL3_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(2, 3);
|
||||
#elif CONFIG_GATEWAY_CHANNEL3_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(2, 3, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL3_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(2, 3, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 4
|
||||
#if CONFIG_GATEWAY_CHANNEL4_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(3, 4);
|
||||
#elif CONFIG_GATEWAY_CHANNEL4_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(3, 4, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL4_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(3, 4, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 5
|
||||
#if CONFIG_GATEWAY_CHANNEL5_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(4, 5);
|
||||
#elif CONFIG_GATEWAY_CHANNEL5_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(4, 5, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL5_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(4, 5, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 6
|
||||
#if CONFIG_GATEWAY_CHANNEL6_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(5, 6);
|
||||
#elif CONFIG_GATEWAY_CHANNEL6_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(5, 6, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL6_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(5, 6, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 7
|
||||
#if CONFIG_GATEWAY_CHANNEL7_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(6, 7);
|
||||
#elif CONFIG_GATEWAY_CHANNEL7_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(6, 7, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL7_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(6, 7, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 8
|
||||
#if CONFIG_GATEWAY_CHANNEL8_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(7, 8);
|
||||
#elif CONFIG_GATEWAY_CHANNEL8_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(7, 8, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL8_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(7, 8, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 9
|
||||
#if CONFIG_GATEWAY_CHANNEL9_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(8, 9);
|
||||
#elif CONFIG_GATEWAY_CHANNEL9_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(8, 9, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL9_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(8, 9, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 10
|
||||
#if CONFIG_GATEWAY_CHANNEL10_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(9, 10);
|
||||
#elif CONFIG_GATEWAY_CHANNEL10_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(9, 10, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL10_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(9, 10, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 11
|
||||
#if CONFIG_GATEWAY_CHANNEL11_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(10, 11);
|
||||
#elif CONFIG_GATEWAY_CHANNEL11_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(10, 11, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL11_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(10, 11, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 12
|
||||
#if CONFIG_GATEWAY_CHANNEL12_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(11, 12);
|
||||
#elif CONFIG_GATEWAY_CHANNEL12_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(11, 12, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL12_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(11, 12, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 13
|
||||
#if CONFIG_GATEWAY_CHANNEL13_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(12, 13);
|
||||
#elif CONFIG_GATEWAY_CHANNEL13_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(12, 13, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL13_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(12, 13, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 14
|
||||
#if CONFIG_GATEWAY_CHANNEL14_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(13, 14);
|
||||
#elif CONFIG_GATEWAY_CHANNEL14_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(13, 14, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL14_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(13, 14, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 15
|
||||
#if CONFIG_GATEWAY_CHANNEL15_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(14, 15);
|
||||
#elif CONFIG_GATEWAY_CHANNEL15_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(14, 15, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL15_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(14, 15, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 16
|
||||
#if CONFIG_GATEWAY_CHANNEL16_PHY_NATIVE
|
||||
GATEWAY_CONFIGURE_NATIVE_CHANNEL(15, 16);
|
||||
#elif CONFIG_GATEWAY_CHANNEL16_PHY_UART1
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(15, 16, 1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL16_PHY_UART2
|
||||
GATEWAY_CONFIGURE_SERIAL_CHANNEL(15, 16, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return channels;
|
||||
}
|
||||
|
||||
bool ValidateChannelBindings() {
|
||||
if (k485ControlEnabled && kConsoleOnUart0) {
|
||||
ESP_LOGE(kTag, "485 control bridge requires moving the ESP-IDF console off UART0");
|
||||
return false;
|
||||
}
|
||||
|
||||
ChannelBindingConfig channels[CONFIG_GATEWAY_CHANNEL_COUNT] = {};
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE
|
||||
channels[0].enabled = true;
|
||||
channels[0].native_phy = true;
|
||||
channels[0].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
|
||||
channels[0].native_bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_BUS_ID);
|
||||
channels[0].native_baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE);
|
||||
#elif CONFIG_GATEWAY_CHANNEL1_PHY_UART1
|
||||
channels[0].enabled = true;
|
||||
channels[0].serial_phy = true;
|
||||
channels[0].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
|
||||
channels[0].uart_port = 1;
|
||||
#elif CONFIG_GATEWAY_CHANNEL1_PHY_UART2
|
||||
channels[0].enabled = true;
|
||||
channels[0].serial_phy = true;
|
||||
channels[0].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
|
||||
channels[0].uart_port = 2;
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 2
|
||||
#if CONFIG_GATEWAY_CHANNEL2_PHY_NATIVE
|
||||
channels[1].enabled = true;
|
||||
channels[1].native_phy = true;
|
||||
channels[1].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
|
||||
channels[1].native_bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_BUS_ID);
|
||||
channels[1].native_baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_BAUDRATE);
|
||||
#elif CONFIG_GATEWAY_CHANNEL2_PHY_UART1
|
||||
channels[1].enabled = true;
|
||||
channels[1].serial_phy = true;
|
||||
channels[1].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
|
||||
channels[1].uart_port = 1;
|
||||
#elif CONFIG_GATEWAY_CHANNEL2_PHY_UART2
|
||||
channels[1].enabled = true;
|
||||
channels[1].serial_phy = true;
|
||||
channels[1].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
|
||||
channels[1].uart_port = 2;
|
||||
#endif
|
||||
#endif
|
||||
const auto channels = BuildChannelBindings();
|
||||
|
||||
bool any_enabled = false;
|
||||
bool saw_native = false;
|
||||
@@ -546,6 +779,11 @@ bool ValidateChannelBindings() {
|
||||
continue;
|
||||
}
|
||||
any_enabled = true;
|
||||
if (channels[i].native_phy &&
|
||||
(channels[i].native_tx_pin < 0 || channels[i].native_rx_pin < 0)) {
|
||||
ESP_LOGE(kTag, "native DALI channel %d requires TX and RX GPIO pins", i + 1);
|
||||
return false;
|
||||
}
|
||||
for (int j = i + 1; j < CONFIG_GATEWAY_CHANNEL_COUNT; ++j) {
|
||||
if (!channels[j].enabled) {
|
||||
continue;
|
||||
@@ -606,6 +844,22 @@ bool ValidateChannelBindings() {
|
||||
|
||||
if (kKnxBridgeSupported) {
|
||||
const int knx_uart = CONFIG_GATEWAY_KNX_TP_UART_PORT;
|
||||
if (kKnxBridgeStartupEnabled) {
|
||||
const uint8_t knx_dali_bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_KNX_DALI_BUS_ID);
|
||||
int matches = 0;
|
||||
for (int i = 0; i < CONFIG_GATEWAY_CHANNEL_COUNT; ++i) {
|
||||
if (channels[i].enabled && channels[i].native_phy &&
|
||||
channels[i].native_bus_id == knx_dali_bus_id) {
|
||||
++matches;
|
||||
}
|
||||
}
|
||||
if (matches != 1) {
|
||||
ESP_LOGE(kTag,
|
||||
"KNX DALI bus id %u must match exactly one enabled native DALI channel",
|
||||
knx_dali_bus_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (knx_uart >= 0 && k485ControlEnabled && knx_uart == 0) {
|
||||
ESP_LOGE(kTag, "KNX TP UART0 conflicts with the UART0 control bridge");
|
||||
return false;
|
||||
@@ -639,83 +893,43 @@ bool ValidateChannelBindings() {
|
||||
|
||||
esp_err_t BindConfiguredChannels(gateway::DaliDomainService& dali_domain,
|
||||
const gateway::GatewayRuntime& runtime) {
|
||||
#if CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE
|
||||
gateway::DaliHardwareBusConfig channel1{};
|
||||
channel1.channel_index = 0;
|
||||
channel1.gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
|
||||
channel1.bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_BUS_ID);
|
||||
channel1.tx_pin = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_TX_PIN);
|
||||
channel1.rx_pin = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_RX_PIN);
|
||||
channel1.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE);
|
||||
channel1.name = runtime.gatewayName(channel1.gateway_id);
|
||||
esp_err_t err = dali_domain.bindHardwareBus(channel1);
|
||||
LogBindError("channel1 native DALI", err);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
for (const auto& channel : BuildChannelBindings()) {
|
||||
if (!channel.enabled) {
|
||||
continue;
|
||||
}
|
||||
if (channel.native_phy) {
|
||||
gateway::DaliHardwareBusConfig config{};
|
||||
config.channel_index = channel.channel_index;
|
||||
config.gateway_id = channel.gateway_id;
|
||||
config.bus_id = channel.native_bus_id;
|
||||
config.tx_pin = static_cast<uint8_t>(channel.native_tx_pin);
|
||||
config.rx_pin = static_cast<uint8_t>(channel.native_rx_pin);
|
||||
config.baudrate = channel.native_baudrate;
|
||||
config.name = runtime.gatewayName(config.gateway_id);
|
||||
const esp_err_t err = dali_domain.bindHardwareBus(config);
|
||||
LogBindError(config.name.c_str(), err);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
} else if (channel.serial_phy) {
|
||||
gateway::DaliSerialBusConfig config{};
|
||||
config.channel_index = channel.channel_index;
|
||||
config.gateway_id = channel.gateway_id;
|
||||
config.uart_port = channel.uart_port;
|
||||
config.tx_pin = channel.serial_tx_pin;
|
||||
config.rx_pin = channel.serial_rx_pin;
|
||||
config.baudrate = channel.serial_baudrate;
|
||||
config.rx_buffer_size = channel.serial_rx_buffer_size;
|
||||
config.tx_buffer_size = channel.serial_tx_buffer_size;
|
||||
config.query_timeout_ms = channel.serial_query_timeout_ms;
|
||||
config.name = runtime.gatewayName(config.gateway_id);
|
||||
const esp_err_t err = dali_domain.bindSerialBus(config);
|
||||
LogBindError(config.name.c_str(), err);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif CONFIG_GATEWAY_CHANNEL1_PHY_UART1 || CONFIG_GATEWAY_CHANNEL1_PHY_UART2
|
||||
gateway::DaliSerialBusConfig channel1{};
|
||||
channel1.channel_index = 0;
|
||||
channel1.gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
|
||||
#if CONFIG_GATEWAY_CHANNEL1_PHY_UART1
|
||||
channel1.uart_port = 1;
|
||||
#else
|
||||
channel1.uart_port = 2;
|
||||
#endif
|
||||
channel1.tx_pin = CONFIG_GATEWAY_CHANNEL1_SERIAL_TX_PIN;
|
||||
channel1.rx_pin = CONFIG_GATEWAY_CHANNEL1_SERIAL_RX_PIN;
|
||||
channel1.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL1_SERIAL_BAUDRATE);
|
||||
channel1.rx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_CHANNEL1_SERIAL_RX_BUFFER);
|
||||
channel1.tx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_CHANNEL1_SERIAL_TX_BUFFER);
|
||||
channel1.query_timeout_ms =
|
||||
static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL1_SERIAL_QUERY_TIMEOUT_MS);
|
||||
channel1.name = runtime.gatewayName(channel1.gateway_id);
|
||||
esp_err_t err1 = dali_domain.bindSerialBus(channel1);
|
||||
LogBindError("channel1 serial DALI", err1);
|
||||
if (err1 != ESP_OK) {
|
||||
return err1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 2
|
||||
#if CONFIG_GATEWAY_CHANNEL2_PHY_NATIVE
|
||||
gateway::DaliHardwareBusConfig channel2{};
|
||||
channel2.channel_index = 1;
|
||||
channel2.gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
|
||||
channel2.bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_BUS_ID);
|
||||
channel2.tx_pin = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_TX_PIN);
|
||||
channel2.rx_pin = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_RX_PIN);
|
||||
channel2.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_BAUDRATE);
|
||||
channel2.name = runtime.gatewayName(channel2.gateway_id);
|
||||
esp_err_t err2 = dali_domain.bindHardwareBus(channel2);
|
||||
LogBindError("channel2 native DALI", err2);
|
||||
if (err2 != ESP_OK) {
|
||||
return err2;
|
||||
}
|
||||
#elif CONFIG_GATEWAY_CHANNEL2_PHY_UART1 || CONFIG_GATEWAY_CHANNEL2_PHY_UART2
|
||||
gateway::DaliSerialBusConfig channel2{};
|
||||
channel2.channel_index = 1;
|
||||
channel2.gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
|
||||
#if CONFIG_GATEWAY_CHANNEL2_PHY_UART1
|
||||
channel2.uart_port = 1;
|
||||
#else
|
||||
channel2.uart_port = 2;
|
||||
#endif
|
||||
channel2.tx_pin = CONFIG_GATEWAY_CHANNEL2_SERIAL_TX_PIN;
|
||||
channel2.rx_pin = CONFIG_GATEWAY_CHANNEL2_SERIAL_RX_PIN;
|
||||
channel2.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL2_SERIAL_BAUDRATE);
|
||||
channel2.rx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_CHANNEL2_SERIAL_RX_BUFFER);
|
||||
channel2.tx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_CHANNEL2_SERIAL_TX_BUFFER);
|
||||
channel2.query_timeout_ms =
|
||||
static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL2_SERIAL_QUERY_TIMEOUT_MS);
|
||||
channel2.name = runtime.gatewayName(channel2.gateway_id);
|
||||
esp_err_t err2 = dali_domain.bindSerialBus(channel2);
|
||||
LogBindError("channel2 serial DALI", err2);
|
||||
if (err2 != ESP_OK) {
|
||||
return err2;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -761,11 +975,15 @@ extern "C" void app_main(void) {
|
||||
cache_config.full_state_mirror_enabled = cache_config.reconciliation_enabled &&
|
||||
kCacheFullStateMirrorEnabled;
|
||||
cache_config.flush_interval_ms = static_cast<uint32_t>(CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS);
|
||||
cache_config.refresh_interval_ms =
|
||||
static_cast<uint32_t>(CONFIG_GATEWAY_CACHE_REFRESH_INTERVAL_MS);
|
||||
cache_config.default_priority_mode = kCachePriorityMode;
|
||||
s_cache = std::make_unique<gateway::GatewayCache>(cache_config);
|
||||
ESP_ERROR_CHECK(s_cache->start());
|
||||
|
||||
gateway::GatewayControllerConfig controller_config;
|
||||
controller_config.task_stack_size =
|
||||
static_cast<uint32_t>(CONFIG_GATEWAY_CONTROLLER_TASK_STACK_SIZE);
|
||||
const bool network_transport_supported = profile.enable_wifi || profile.enable_eth;
|
||||
controller_config.setup_supported = true;
|
||||
controller_config.ble_supported = profile.enable_ble;
|
||||
@@ -773,6 +991,9 @@ extern "C" void app_main(void) {
|
||||
controller_config.ip_router_supported = network_transport_supported;
|
||||
controller_config.internal_scene_supported = true;
|
||||
controller_config.internal_group_supported = true;
|
||||
controller_config.cache_supported = kCacheSupported;
|
||||
controller_config.cache_refresh_interval_ms =
|
||||
static_cast<uint32_t>(CONFIG_GATEWAY_CACHE_REFRESH_INTERVAL_MS);
|
||||
|
||||
s_controller = std::make_unique<gateway::GatewayController>(*s_runtime, *s_dali_domain,
|
||||
*s_cache,
|
||||
@@ -819,18 +1040,11 @@ extern "C" void app_main(void) {
|
||||
static_cast<UBaseType_t>(CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_PRIORITY);
|
||||
bridge_config.allow_modbus_uart0 = kModbusAllowUart0 && !kConsoleOnUart0 && !k485ControlEnabled;
|
||||
bridge_config.allow_knx_uart0 = !kConsoleOnUart0 && !k485ControlEnabled;
|
||||
#if CONFIG_GATEWAY_CHANNEL1_PHY_UART1
|
||||
bridge_config.reserved_uart_ports.push_back(1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL1_PHY_UART2
|
||||
bridge_config.reserved_uart_ports.push_back(2);
|
||||
#endif
|
||||
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 2
|
||||
#if CONFIG_GATEWAY_CHANNEL2_PHY_UART1
|
||||
bridge_config.reserved_uart_ports.push_back(1);
|
||||
#elif CONFIG_GATEWAY_CHANNEL2_PHY_UART2
|
||||
bridge_config.reserved_uart_ports.push_back(2);
|
||||
#endif
|
||||
#endif
|
||||
for (const auto& channel : BuildChannelBindings()) {
|
||||
if (channel.enabled && channel.serial_phy) {
|
||||
bridge_config.reserved_uart_ports.push_back(channel.uart_port);
|
||||
}
|
||||
}
|
||||
if (kModbusBridgeSupported) {
|
||||
gateway::GatewayModbusConfig default_modbus;
|
||||
#if defined(CONFIG_GATEWAY_MODBUS_DEFAULT_TRANSPORT_RTU)
|
||||
@@ -864,12 +1078,35 @@ extern "C" void app_main(void) {
|
||||
default_knx.tunnel_enabled = kKnxTunnelEnabled;
|
||||
default_knx.multicast_enabled = kKnxMulticastEnabled;
|
||||
default_knx.main_group = static_cast<uint8_t>(CONFIG_GATEWAY_KNX_MAIN_GROUP);
|
||||
default_knx.dali_bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_KNX_DALI_BUS_ID);
|
||||
default_knx.udp_port = static_cast<uint16_t>(CONFIG_GATEWAY_KNX_UDP_PORT);
|
||||
default_knx.multicast_address = CONFIG_GATEWAY_KNX_MULTICAST_ADDRESS;
|
||||
default_knx.ip_interface_individual_address =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_IP_INTERFACE_INDIVIDUAL_ADDRESS);
|
||||
default_knx.individual_address =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_INDIVIDUAL_ADDRESS);
|
||||
default_knx.oam_router.enabled = kKnxOamRouterSupported && kKnxOamRouterEnabled;
|
||||
default_knx.oam_router.individual_address =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_INDIVIDUAL_ADDRESS);
|
||||
default_knx.oam_router.tunnel_address_base =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_TUNNEL_ADDRESS_BASE);
|
||||
if (default_knx.oam_router.enabled) {
|
||||
default_knx.ip_interface_individual_address = default_knx.oam_router.individual_address;
|
||||
}
|
||||
default_knx.oam_router.programming_button_gpio =
|
||||
CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_BUTTON_GPIO;
|
||||
default_knx.oam_router.programming_led_gpio =
|
||||
CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_LED_GPIO;
|
||||
#ifdef CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_BUTTON_ACTIVE_LOW
|
||||
default_knx.oam_router.programming_button_active_low = true;
|
||||
#else
|
||||
default_knx.oam_router.programming_button_active_low = false;
|
||||
#endif
|
||||
#ifdef CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_LED_ACTIVE_HIGH
|
||||
default_knx.oam_router.programming_led_active_high = true;
|
||||
#else
|
||||
default_knx.oam_router.programming_led_active_high = false;
|
||||
#endif
|
||||
default_knx.programming_button_gpio = CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO;
|
||||
default_knx.programming_led_gpio = CONFIG_GATEWAY_KNX_PROGRAMMING_LED_GPIO;
|
||||
#ifdef CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_ACTIVE_LOW
|
||||
@@ -886,6 +1123,8 @@ extern "C" void app_main(void) {
|
||||
default_knx.tp_uart.tx_pin = CONFIG_GATEWAY_KNX_TP_TX_PIN;
|
||||
default_knx.tp_uart.rx_pin = CONFIG_GATEWAY_KNX_TP_RX_PIN;
|
||||
default_knx.tp_uart.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_KNX_TP_BAUDRATE);
|
||||
default_knx.tp_uart.startup_timeout_ms =
|
||||
static_cast<uint32_t>(CONFIG_GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS);
|
||||
#ifdef CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE
|
||||
default_knx.tp_uart.nine_bit_mode = true;
|
||||
#else
|
||||
@@ -899,6 +1138,7 @@ extern "C" void app_main(void) {
|
||||
static_cast<UBaseType_t>(CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY);
|
||||
s_bridge = std::make_unique<gateway::GatewayBridgeService>(*s_dali_domain, *s_cache,
|
||||
bridge_config);
|
||||
s_controller->setBridgeService(s_bridge.get());
|
||||
}
|
||||
|
||||
if (profile.enable_wifi || profile.enable_eth) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
otadata, data, ota, 0xf000, 0x2000,
|
||||
phy_init, data, phy, 0x11000, 0x1000,
|
||||
factory, app, factory, 0x20000, 0x280000,
|
||||
storage, data, spiffs, 0x300000, 0x100000,
|
||||
nvs, data, nvs, 0x9000, 0x14000,
|
||||
otadata, data, ota, 0x1D000, 0x2000,
|
||||
phy_init, data, phy, 0x1F000, 0x1000,
|
||||
factory, app, factory, 0x20000, 0x300000,
|
||||
knxprops, data, 0x40, 0x320000, 0x60000,
|
||||
storage, data, spiffs, 0x380000, 0x80000,
|
||||
|
||||
|
+204
-55
@@ -596,12 +596,16 @@ CONFIG_PARTITION_TABLE_MD5=y
|
||||
#
|
||||
# Gateway App
|
||||
#
|
||||
|
||||
#
|
||||
# DALI Settings
|
||||
#
|
||||
CONFIG_GATEWAY_CHANNEL_COUNT=1
|
||||
|
||||
#
|
||||
# Gateway Channel 1
|
||||
#
|
||||
CONFIG_GATEWAY_CHANNEL1_GW_ID=3
|
||||
CONFIG_GATEWAY_CHANNEL1_GW_ID=0
|
||||
# CONFIG_GATEWAY_CHANNEL1_PHY_DISABLED is not set
|
||||
CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE=y
|
||||
# CONFIG_GATEWAY_CHANNEL1_PHY_UART1 is not set
|
||||
@@ -617,22 +621,95 @@ CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE=1200
|
||||
#
|
||||
# end of Gateway Channel 2
|
||||
|
||||
#
|
||||
# Gateway Channel 3
|
||||
#
|
||||
# end of Gateway Channel 3
|
||||
|
||||
#
|
||||
# Gateway Channel 4
|
||||
#
|
||||
# end of Gateway Channel 4
|
||||
|
||||
#
|
||||
# Gateway Channel 5
|
||||
#
|
||||
# end of Gateway Channel 5
|
||||
|
||||
#
|
||||
# Gateway Channel 6
|
||||
#
|
||||
# end of Gateway Channel 6
|
||||
|
||||
#
|
||||
# Gateway Channel 7
|
||||
#
|
||||
# end of Gateway Channel 7
|
||||
|
||||
#
|
||||
# Gateway Channel 8
|
||||
#
|
||||
# end of Gateway Channel 8
|
||||
|
||||
#
|
||||
# Gateway Channel 9
|
||||
#
|
||||
# end of Gateway Channel 9
|
||||
|
||||
#
|
||||
# Gateway Channel 10
|
||||
#
|
||||
# end of Gateway Channel 10
|
||||
|
||||
#
|
||||
# Gateway Channel 11
|
||||
#
|
||||
# end of Gateway Channel 11
|
||||
|
||||
#
|
||||
# Gateway Channel 12
|
||||
#
|
||||
# end of Gateway Channel 12
|
||||
|
||||
#
|
||||
# Gateway Channel 13
|
||||
#
|
||||
# end of Gateway Channel 13
|
||||
|
||||
#
|
||||
# Gateway Channel 14
|
||||
#
|
||||
# end of Gateway Channel 14
|
||||
|
||||
#
|
||||
# Gateway Channel 15
|
||||
#
|
||||
# end of Gateway Channel 15
|
||||
|
||||
#
|
||||
# Gateway Channel 16
|
||||
#
|
||||
# end of Gateway Channel 16
|
||||
|
||||
#
|
||||
# Gateway Cache
|
||||
#
|
||||
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_FLUSH_INTERVAL_MS=60000
|
||||
CONFIG_GATEWAY_CACHE_FULL_STATE_MIRROR=y
|
||||
CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS=600000
|
||||
CONFIG_GATEWAY_CACHE_REFRESH_INTERVAL_MS=120000
|
||||
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
|
||||
CONFIG_GATEWAY_CONTROLLER_TASK_STACK_SIZE=12288
|
||||
# end of DALI Settings
|
||||
|
||||
#
|
||||
# Gateway Startup Services
|
||||
# Connectivity Startup
|
||||
#
|
||||
CONFIG_GATEWAY_BLE_SUPPORTED=y
|
||||
CONFIG_GATEWAY_START_BLE_ENABLED=y
|
||||
@@ -646,6 +723,7 @@ CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC=60
|
||||
CONFIG_GATEWAY_ETHERNET_SUPPORTED=y
|
||||
CONFIG_GATEWAY_START_ETHERNET_ENABLED=y
|
||||
CONFIG_GATEWAY_ETHERNET_IGNORE_INIT_FAILURE=y
|
||||
# end of Connectivity Startup
|
||||
|
||||
#
|
||||
# Gateway Wired Ethernet
|
||||
@@ -657,13 +735,21 @@ CONFIG_GATEWAY_ETHERNET_W5500_MISO_GPIO=33
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_CS_GPIO=34
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_INT_GPIO=36
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_POLL_PERIOD_MS=0
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_CLOCK_MHZ=40
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_CLOCK_MHZ=20
|
||||
CONFIG_GATEWAY_ETHERNET_PHY_RESET_GPIO=-1
|
||||
CONFIG_GATEWAY_ETHERNET_PHY_ADDR=1
|
||||
CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE=4096
|
||||
# end of Gateway Wired Ethernet
|
||||
|
||||
#
|
||||
# Bridge Runtime
|
||||
#
|
||||
CONFIG_GATEWAY_BRIDGE_SUPPORTED=y
|
||||
# end of Bridge Runtime
|
||||
|
||||
#
|
||||
# Modbus Settings
|
||||
#
|
||||
CONFIG_GATEWAY_MODBUS_BRIDGE_SUPPORTED=y
|
||||
# CONFIG_GATEWAY_START_MODBUS_BRIDGE_ENABLED is not set
|
||||
CONFIG_GATEWAY_MODBUS_DEFAULT_TRANSPORT_TCP=y
|
||||
@@ -671,21 +757,48 @@ CONFIG_GATEWAY_MODBUS_DEFAULT_TRANSPORT_TCP=y
|
||||
# CONFIG_GATEWAY_MODBUS_DEFAULT_TRANSPORT_ASCII is not set
|
||||
CONFIG_GATEWAY_MODBUS_TCP_PORT=1502
|
||||
CONFIG_GATEWAY_MODBUS_UNIT_ID=1
|
||||
# end of Modbus Settings
|
||||
|
||||
#
|
||||
# BACnet Settings
|
||||
#
|
||||
CONFIG_GATEWAY_BACNET_BRIDGE_SUPPORTED=y
|
||||
# CONFIG_GATEWAY_START_BACNET_BRIDGE_ENABLED is not set
|
||||
# end of BACnet Settings
|
||||
|
||||
#
|
||||
# KNX Settings
|
||||
#
|
||||
CONFIG_GATEWAY_KNX_INSTANCE_COUNT=1
|
||||
CONFIG_GATEWAY_KNX_BRIDGE_SUPPORTED=y
|
||||
CONFIG_GATEWAY_START_KNX_BRIDGE_ENABLED=y
|
||||
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_IP_SECURE_SUPPORTED=y
|
||||
CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS=y
|
||||
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_DALI_BUS_ID=0
|
||||
# CONFIG_GATEWAY_KNX_DEBUG_DUMP_MEMORY is not set
|
||||
CONFIG_GATEWAY_KNX_TUNNEL_ENABLED=y
|
||||
CONFIG_GATEWAY_KNX_MULTICAST_ENABLED=y
|
||||
CONFIG_GATEWAY_KNX_UDP_PORT=3671
|
||||
CONFIG_GATEWAY_KNX_MULTICAST_ADDRESS="224.0.23.12"
|
||||
CONFIG_GATEWAY_KNX_IP_INTERFACE_INDIVIDUAL_ADDRESS=65281
|
||||
CONFIG_GATEWAY_KNX_INDIVIDUAL_ADDRESS=65534
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_SUPPORTED=y
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_ENABLED=y
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_OEM_MANUFACTURER_ID=0x00FA
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_HARDWARE_ID=0x0001
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_NUMBER=0xA11F
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_VERSION=0x07
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_INDIVIDUAL_ADDRESS=65282
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_TUNNEL_ADDRESS_BASE=65296
|
||||
CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_BUTTON_GPIO=-1
|
||||
CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_LED_GPIO=-1
|
||||
CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO=0
|
||||
CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_ACTIVE_LOW=y
|
||||
CONFIG_GATEWAY_KNX_PROGRAMMING_LED_GPIO=10
|
||||
@@ -694,19 +807,49 @@ CONFIG_GATEWAY_KNX_TP_UART_PORT=0
|
||||
CONFIG_GATEWAY_KNX_TP_TX_PIN=-1
|
||||
CONFIG_GATEWAY_KNX_TP_RX_PIN=-1
|
||||
CONFIG_GATEWAY_KNX_TP_BAUDRATE=19200
|
||||
CONFIG_GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS=2000
|
||||
CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE=y
|
||||
CONFIG_GATEWAY_KNX_TP_FULL_IP_FORWARD=y
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5
|
||||
# end of KNX Settings
|
||||
|
||||
#
|
||||
# Cloud Settings
|
||||
#
|
||||
CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED=y
|
||||
# CONFIG_GATEWAY_START_CLOUD_BRIDGE_ENABLED is not set
|
||||
CONFIG_GATEWAY_CLOUD_TOPIC_PREFIX="devices"
|
||||
CONFIG_GATEWAY_CLOUD_CEMI_TRANSPORT_MQTT=y
|
||||
# CONFIG_GATEWAY_CLOUD_CEMI_TRANSPORT_LTE_UART is not set
|
||||
# CONFIG_GATEWAY_CLOUD_CEMI_TRANSPORT_MQTT_AND_LTE_UART is not set
|
||||
CONFIG_GATEWAY_CLOUD_LTE_UART_PORT=-1
|
||||
CONFIG_GATEWAY_CLOUD_LTE_UART_TX_PIN=-1
|
||||
CONFIG_GATEWAY_CLOUD_LTE_UART_RX_PIN=-1
|
||||
CONFIG_GATEWAY_CLOUD_LTE_UART_BAUDRATE=115200
|
||||
# end of Cloud Settings
|
||||
|
||||
#
|
||||
# Bridge Task Settings
|
||||
#
|
||||
CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_STACK_SIZE=6144
|
||||
CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_PRIORITY=4
|
||||
CONFIG_GATEWAY_BRIDGE_BACNET_TASK_STACK_SIZE=8192
|
||||
CONFIG_GATEWAY_BRIDGE_BACNET_TASK_PRIORITY=5
|
||||
# end of Bridge Task Settings
|
||||
|
||||
#
|
||||
# USB Setup
|
||||
#
|
||||
CONFIG_GATEWAY_USB_STARTUP_DEBUG_JTAG=y
|
||||
# CONFIG_GATEWAY_USB_STARTUP_SETUP_SERIAL is not set
|
||||
# end of USB Setup
|
||||
|
||||
#
|
||||
# UART0 Control
|
||||
#
|
||||
# CONFIG_GATEWAY_485_CONTROL_ENABLED is not set
|
||||
# end of Gateway Startup Services
|
||||
# end of UART0 Control
|
||||
|
||||
#
|
||||
# Gateway Network Services
|
||||
@@ -794,12 +937,12 @@ CONFIG_BT_CONTROLLER_ENABLED=y
|
||||
#
|
||||
# General
|
||||
#
|
||||
CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y
|
||||
# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL is not set
|
||||
# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL is not set
|
||||
CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL=y
|
||||
# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE=0
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y
|
||||
# CONFIG_BT_NIMBLE_PINNED_TO_CORE_1 is not set
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE=1
|
||||
# CONFIG_BT_NIMBLE_PINNED_TO_CORE_0 is not set
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y
|
||||
CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=4096
|
||||
CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE=y
|
||||
# end of General
|
||||
@@ -841,7 +984,7 @@ CONFIG_BT_NIMBLE_MAX_CONN_REATTEMPT=3
|
||||
CONFIG_BT_NIMBLE_HS_PVCY=y
|
||||
# CONFIG_BT_NIMBLE_HOST_ALLOW_CONNECT_WITH_SCAN is not set
|
||||
# CONFIG_BT_NIMBLE_HOST_QUEUE_CONG_CHECK is not set
|
||||
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=3
|
||||
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4
|
||||
CONFIG_BT_NIMBLE_MAX_CCCDS=8
|
||||
CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS=y
|
||||
CONFIG_BT_NIMBLE_HS_STOP_TIMEOUT_MS=2000
|
||||
@@ -1004,10 +1147,10 @@ CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23
|
||||
# CONFIG_BT_NIMBLE_MEM_DEBUG is not set
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_NONE is not set
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR is not set
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING is not set
|
||||
CONFIG_BT_NIMBLE_LOG_LEVEL_INFO=y
|
||||
CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING=y
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_INFO is not set
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG is not set
|
||||
CONFIG_BT_NIMBLE_LOG_LEVEL=1
|
||||
CONFIG_BT_NIMBLE_LOG_LEVEL=2
|
||||
CONFIG_BT_NIMBLE_PRINT_ERR_NAME=y
|
||||
# CONFIG_BT_NIMBLE_DEBUG is not set
|
||||
# CONFIG_BT_NIMBLE_TEST_THROUGHPUT_TEST is not set
|
||||
@@ -1036,9 +1179,9 @@ CONFIG_BT_CTRL_MODE_EFF=1
|
||||
CONFIG_BT_CTRL_BLE_MAX_ACT=6
|
||||
CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=6
|
||||
CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0
|
||||
CONFIG_BT_CTRL_PINNED_TO_CORE_0=y
|
||||
# CONFIG_BT_CTRL_PINNED_TO_CORE_1 is not set
|
||||
CONFIG_BT_CTRL_PINNED_TO_CORE=0
|
||||
# CONFIG_BT_CTRL_PINNED_TO_CORE_0 is not set
|
||||
CONFIG_BT_CTRL_PINNED_TO_CORE_1=y
|
||||
CONFIG_BT_CTRL_PINNED_TO_CORE=1
|
||||
CONFIG_BT_CTRL_HCI_MODE_VHCI=y
|
||||
# CONFIG_BT_CTRL_HCI_MODE_UART_H4 is not set
|
||||
CONFIG_BT_CTRL_HCI_TL=1
|
||||
@@ -1301,15 +1444,15 @@ CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
|
||||
#
|
||||
# ESP-Driver:GPIO Configurations
|
||||
#
|
||||
# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set
|
||||
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
|
||||
# end of ESP-Driver:GPIO Configurations
|
||||
|
||||
#
|
||||
# ESP-Driver:GPTimer Configurations
|
||||
#
|
||||
CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
|
||||
# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set
|
||||
# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set
|
||||
CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=y
|
||||
CONFIG_GPTIMER_ISR_CACHE_SAFE=y
|
||||
CONFIG_GPTIMER_OBJ_CACHE_SAFE=y
|
||||
# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set
|
||||
# end of ESP-Driver:GPTimer Configurations
|
||||
@@ -1642,7 +1785,7 @@ CONFIG_ESP_NETIF_TCPIP_LWIP=y
|
||||
# CONFIG_ESP_NETIF_LOOPBACK is not set
|
||||
CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y
|
||||
CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC=y
|
||||
# CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS is not set
|
||||
CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS=y
|
||||
# CONFIG_ESP_NETIF_L2_TAP is not set
|
||||
# CONFIG_ESP_NETIF_BRIDGE_EN is not set
|
||||
# CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF is not set
|
||||
@@ -1745,9 +1888,9 @@ CONFIG_ESP_ROM_PRINT_IN_IRAM=y
|
||||
# ESP System Settings
|
||||
#
|
||||
# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
|
||||
# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160
|
||||
# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160 is not set
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240
|
||||
|
||||
#
|
||||
# Cache config
|
||||
@@ -1793,7 +1936,7 @@ CONFIG_ESP_SYSTEM_IN_IRAM=y
|
||||
CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y
|
||||
# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set
|
||||
# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set
|
||||
CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0
|
||||
CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=2
|
||||
CONFIG_ESP_SYSTEM_RTC_FAST_MEM_AS_HEAP_DEPCHECK=y
|
||||
CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y
|
||||
|
||||
@@ -1873,9 +2016,9 @@ CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
|
||||
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y
|
||||
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1
|
||||
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32
|
||||
CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y
|
||||
# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set
|
||||
CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0
|
||||
# CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER is not set
|
||||
CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER=y
|
||||
CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=1
|
||||
CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5
|
||||
# CONFIG_ESP_WIFI_CSI_ENABLED is not set
|
||||
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
|
||||
@@ -1883,8 +2026,8 @@ CONFIG_ESP_WIFI_TX_BA_WIN=6
|
||||
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP_WIFI_RX_BA_WIN=6
|
||||
CONFIG_ESP_WIFI_NVS_ENABLED=y
|
||||
CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y
|
||||
# CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1 is not set
|
||||
# CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0 is not set
|
||||
CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1=y
|
||||
CONFIG_ESP_WIFI_SOFTAP_BEACON_MAX_LEN=752
|
||||
CONFIG_ESP_WIFI_MGMT_SBUF_NUM=32
|
||||
CONFIG_ESP_WIFI_IRAM_OPT=y
|
||||
@@ -2001,12 +2144,12 @@ CONFIG_FATFS_DONT_TRUST_LAST_ALLOC=0
|
||||
#
|
||||
# CONFIG_FREERTOS_SMP is not set
|
||||
# CONFIG_FREERTOS_UNICORE is not set
|
||||
CONFIG_FREERTOS_HZ=100
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set
|
||||
# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set
|
||||
CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y
|
||||
CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1
|
||||
CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536
|
||||
CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=2048
|
||||
# CONFIG_FREERTOS_USE_IDLE_HOOK is not set
|
||||
# CONFIG_FREERTOS_USE_TICK_HOOK is not set
|
||||
CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
@@ -2771,21 +2914,25 @@ CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
|
||||
#
|
||||
CONFIG_DALI_PHY_COUNT=16
|
||||
CONFIG_DALI_DEFAULT_BAUDRATE=1200
|
||||
CONFIG_DALI_TIMER_RESOLUTION_HZ=3000000
|
||||
CONFIG_DALI_TIMER_RESOLUTION_HZ=3636363
|
||||
CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US=0
|
||||
CONFIG_DALI_TX_STOP_CONDITION_US=0
|
||||
CONFIG_DALI_RX_STOP_CONDITION_US=0
|
||||
CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS=25
|
||||
CONFIG_DALI_DOUBLE_SEND_DELAY_MS=12
|
||||
CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS=30
|
||||
CONFIG_DALI_DOUBLE_SEND_DELAY_MS=20
|
||||
CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS=20
|
||||
CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS=10
|
||||
CONFIG_DALI_FORWARD_MAX_WAIT_MS=50
|
||||
CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS=10
|
||||
CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS=500
|
||||
CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS=3000
|
||||
# CONFIG_DALI_LOG_LEVEL_NONE is not set
|
||||
# CONFIG_DALI_LOG_LEVEL_ERROR is not set
|
||||
# CONFIG_DALI_LOG_LEVEL_WARN is not set
|
||||
# CONFIG_DALI_LOG_LEVEL_INFO is not set
|
||||
# CONFIG_DALI_LOG_LEVEL_DEBUG is not set
|
||||
CONFIG_DALI_LOG_LEVEL_VERBOSE=y
|
||||
CONFIG_DALI_LOG_LEVEL=5
|
||||
CONFIG_DALI_LOG_LEVEL_DEBUG=y
|
||||
# CONFIG_DALI_LOG_LEVEL_VERBOSE is not set
|
||||
CONFIG_DALI_LOG_LEVEL=4
|
||||
CONFIG_DALI_TX_ACTIVE_LOW=y
|
||||
# CONFIG_DALI_TX_ACTIVE_HIGH is not set
|
||||
CONFIG_DALI_RX_ACTIVE_LOW=y
|
||||
@@ -2818,10 +2965,12 @@ CONFIG_MQTT_TRANSPORT_SSL=y
|
||||
CONFIG_MQTT_TRANSPORT_WEBSOCKET=y
|
||||
CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y
|
||||
# CONFIG_MQTT_MSG_ID_INCREMENTAL is not set
|
||||
# CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set
|
||||
CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED=y
|
||||
# CONFIG_MQTT_REPORT_DELETED_MESSAGES is not set
|
||||
# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set
|
||||
# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set
|
||||
CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED=y
|
||||
# CONFIG_MQTT_USE_CORE_0 is not set
|
||||
CONFIG_MQTT_USE_CORE_1=y
|
||||
# CONFIG_MQTT_CUSTOM_OUTBOX is not set
|
||||
# end of ESP-MQTT Configurations
|
||||
# end of Component config
|
||||
@@ -2865,12 +3014,12 @@ CONFIG_ESP32_APPTRACE_DEST_NONE=y
|
||||
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
|
||||
# CONFIG_BLUEDROID_ENABLED is not set
|
||||
CONFIG_NIMBLE_ENABLED=y
|
||||
CONFIG_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y
|
||||
# CONFIG_NIMBLE_MEM_ALLOC_MODE_EXTERNAL is not set
|
||||
# CONFIG_NIMBLE_MEM_ALLOC_MODE_INTERNAL is not set
|
||||
CONFIG_NIMBLE_MEM_ALLOC_MODE_EXTERNAL=y
|
||||
# CONFIG_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set
|
||||
CONFIG_NIMBLE_PINNED_TO_CORE=0
|
||||
CONFIG_NIMBLE_PINNED_TO_CORE_0=y
|
||||
# CONFIG_NIMBLE_PINNED_TO_CORE_1 is not set
|
||||
CONFIG_NIMBLE_PINNED_TO_CORE=1
|
||||
# CONFIG_NIMBLE_PINNED_TO_CORE_0 is not set
|
||||
CONFIG_NIMBLE_PINNED_TO_CORE_1=y
|
||||
CONFIG_NIMBLE_TASK_STACK_SIZE=4096
|
||||
CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096
|
||||
CONFIG_NIMBLE_ROLE_CENTRAL=y
|
||||
@@ -2884,7 +3033,7 @@ CONFIG_BT_NIMBLE_SM_SC_LVL=0
|
||||
# CONFIG_NIMBLE_NVS_PERSIST is not set
|
||||
CONFIG_NIMBLE_MAX_BONDS=3
|
||||
CONFIG_NIMBLE_RPA_TIMEOUT=900
|
||||
CONFIG_NIMBLE_MAX_CONNECTIONS=3
|
||||
CONFIG_NIMBLE_MAX_CONNECTIONS=4
|
||||
CONFIG_NIMBLE_MAX_CCCDS=8
|
||||
CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y
|
||||
# CONFIG_NIMBLE_HS_FLOW_CTRL is not set
|
||||
@@ -2907,7 +3056,7 @@ CONFIG_SW_COEXIST_ENABLE=y
|
||||
CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y
|
||||
CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y
|
||||
# CONFIG_CAM_CTLR_DVP_CAM_ISR_IRAM_SAFE is not set
|
||||
# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set
|
||||
CONFIG_GPTIMER_ISR_IRAM_SAFE=y
|
||||
# CONFIG_MCPWM_ISR_IRAM_SAFE is not set
|
||||
# CONFIG_EVENT_LOOP_PROFILING is not set
|
||||
CONFIG_POST_EVENTS_FROM_ISR=y
|
||||
@@ -2954,9 +3103,9 @@ CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||
CONFIG_DEFAULT_PSRAM_CLK_IO=30
|
||||
CONFIG_DEFAULT_PSRAM_CS_IO=26
|
||||
# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160=y
|
||||
# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240 is not set
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=160
|
||||
# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160 is not set
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240
|
||||
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
|
||||
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
|
||||
CONFIG_MAIN_TASK_STACK_SIZE=8192
|
||||
@@ -2991,8 +3140,8 @@ CONFIG_ESP32_WIFI_TX_BA_WIN=6
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=6
|
||||
CONFIG_ESP32_WIFI_NVS_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
|
||||
# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set
|
||||
# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0 is not set
|
||||
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1=y
|
||||
CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752
|
||||
CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32
|
||||
CONFIG_ESP32_WIFI_IRAM_OPT=y
|
||||
|
||||
+96
-42
@@ -596,7 +596,11 @@ CONFIG_PARTITION_TABLE_MD5=y
|
||||
#
|
||||
# Gateway App
|
||||
#
|
||||
CONFIG_GATEWAY_CHANNEL_COUNT=1
|
||||
|
||||
#
|
||||
# DALI Settings
|
||||
#
|
||||
CONFIG_GATEWAY_CHANNEL_COUNT=2
|
||||
|
||||
#
|
||||
# Gateway Channel 1
|
||||
@@ -615,6 +619,15 @@ CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE=1200
|
||||
#
|
||||
# Gateway Channel 2
|
||||
#
|
||||
CONFIG_GATEWAY_CHANNEL2_GW_ID=4
|
||||
# CONFIG_GATEWAY_CHANNEL2_PHY_DISABLED is not set
|
||||
CONFIG_GATEWAY_CHANNEL2_PHY_NATIVE=y
|
||||
# CONFIG_GATEWAY_CHANNEL2_PHY_UART1 is not set
|
||||
# CONFIG_GATEWAY_CHANNEL2_PHY_UART2 is not set
|
||||
CONFIG_GATEWAY_CHANNEL2_NATIVE_BUS_ID=1
|
||||
CONFIG_GATEWAY_CHANNEL2_NATIVE_TX_PIN=4
|
||||
CONFIG_GATEWAY_CHANNEL2_NATIVE_RX_PIN=3
|
||||
CONFIG_GATEWAY_CHANNEL2_NATIVE_BAUDRATE=1200
|
||||
# end of Gateway Channel 2
|
||||
|
||||
#
|
||||
@@ -623,13 +636,15 @@ 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_FLUSH_INTERVAL_MS=60000
|
||||
CONFIG_GATEWAY_CACHE_FULL_STATE_MIRROR=y
|
||||
CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS=600000
|
||||
CONFIG_GATEWAY_CACHE_REFRESH_INTERVAL_MS=120000
|
||||
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
|
||||
# end of DALI Settings
|
||||
|
||||
#
|
||||
# Gateway Startup Services
|
||||
@@ -657,7 +672,7 @@ CONFIG_GATEWAY_ETHERNET_W5500_MISO_GPIO=33
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_CS_GPIO=34
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_INT_GPIO=36
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_POLL_PERIOD_MS=0
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_CLOCK_MHZ=40
|
||||
CONFIG_GATEWAY_ETHERNET_W5500_CLOCK_MHZ=20
|
||||
CONFIG_GATEWAY_ETHERNET_PHY_RESET_GPIO=-1
|
||||
CONFIG_GATEWAY_ETHERNET_PHY_ADDR=1
|
||||
CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE=4096
|
||||
@@ -673,19 +688,40 @@ CONFIG_GATEWAY_MODBUS_TCP_PORT=1502
|
||||
CONFIG_GATEWAY_MODBUS_UNIT_ID=1
|
||||
CONFIG_GATEWAY_BACNET_BRIDGE_SUPPORTED=y
|
||||
# CONFIG_GATEWAY_START_BACNET_BRIDGE_ENABLED is not set
|
||||
|
||||
#
|
||||
# KNX Settings
|
||||
#
|
||||
CONFIG_GATEWAY_KNX_INSTANCE_COUNT=1
|
||||
CONFIG_GATEWAY_KNX_BRIDGE_SUPPORTED=y
|
||||
CONFIG_GATEWAY_START_KNX_BRIDGE_ENABLED=y
|
||||
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_IP_SECURE_SUPPORTED=y
|
||||
CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS=y
|
||||
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_DALI_BUS_ID=0
|
||||
# CONFIG_GATEWAY_KNX_DEBUG_DUMP_MEMORY is not set
|
||||
CONFIG_GATEWAY_KNX_TUNNEL_ENABLED=y
|
||||
CONFIG_GATEWAY_KNX_MULTICAST_ENABLED=y
|
||||
CONFIG_GATEWAY_KNX_UDP_PORT=3671
|
||||
CONFIG_GATEWAY_KNX_MULTICAST_ADDRESS="224.0.23.12"
|
||||
CONFIG_GATEWAY_KNX_IP_INTERFACE_INDIVIDUAL_ADDRESS=65281
|
||||
CONFIG_GATEWAY_KNX_INDIVIDUAL_ADDRESS=65534
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_SUPPORTED=y
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_ENABLED=y
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_OEM_MANUFACTURER_ID=0x00FA
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_HARDWARE_ID=0x0001
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_NUMBER=0xA11F
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_VERSION=0x07
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_INDIVIDUAL_ADDRESS=65282
|
||||
CONFIG_GATEWAY_KNX_OAM_ROUTER_TUNNEL_ADDRESS_BASE=65296
|
||||
CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_BUTTON_GPIO=-1
|
||||
CONFIG_GATEWAY_KNX_OAM_PROGRAMMING_LED_GPIO=-1
|
||||
CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO=0
|
||||
CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_ACTIVE_LOW=y
|
||||
CONFIG_GATEWAY_KNX_PROGRAMMING_LED_GPIO=10
|
||||
@@ -694,11 +730,23 @@ CONFIG_GATEWAY_KNX_TP_UART_PORT=0
|
||||
CONFIG_GATEWAY_KNX_TP_TX_PIN=-1
|
||||
CONFIG_GATEWAY_KNX_TP_RX_PIN=-1
|
||||
CONFIG_GATEWAY_KNX_TP_BAUDRATE=19200
|
||||
CONFIG_GATEWAY_KNX_TP_STARTUP_TIMEOUT_MS=2000
|
||||
CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE=y
|
||||
CONFIG_GATEWAY_KNX_TP_FULL_IP_FORWARD=y
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288
|
||||
CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5
|
||||
# end of KNX Settings
|
||||
|
||||
CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED=y
|
||||
# CONFIG_GATEWAY_START_CLOUD_BRIDGE_ENABLED is not set
|
||||
CONFIG_GATEWAY_CLOUD_TOPIC_PREFIX="devices"
|
||||
CONFIG_GATEWAY_CLOUD_CEMI_TRANSPORT_MQTT=y
|
||||
# CONFIG_GATEWAY_CLOUD_CEMI_TRANSPORT_LTE_UART is not set
|
||||
# CONFIG_GATEWAY_CLOUD_CEMI_TRANSPORT_MQTT_AND_LTE_UART is not set
|
||||
CONFIG_GATEWAY_CLOUD_LTE_UART_PORT=-1
|
||||
CONFIG_GATEWAY_CLOUD_LTE_UART_TX_PIN=-1
|
||||
CONFIG_GATEWAY_CLOUD_LTE_UART_RX_PIN=-1
|
||||
CONFIG_GATEWAY_CLOUD_LTE_UART_BAUDRATE=115200
|
||||
CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_STACK_SIZE=6144
|
||||
CONFIG_GATEWAY_BRIDGE_MODBUS_TASK_PRIORITY=4
|
||||
CONFIG_GATEWAY_BRIDGE_BACNET_TASK_STACK_SIZE=8192
|
||||
@@ -794,12 +842,12 @@ CONFIG_BT_CONTROLLER_ENABLED=y
|
||||
#
|
||||
# General
|
||||
#
|
||||
CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y
|
||||
# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL is not set
|
||||
# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL is not set
|
||||
CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL=y
|
||||
# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE=0
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y
|
||||
# CONFIG_BT_NIMBLE_PINNED_TO_CORE_1 is not set
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE=1
|
||||
# CONFIG_BT_NIMBLE_PINNED_TO_CORE_0 is not set
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=y
|
||||
CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=4096
|
||||
CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE=y
|
||||
# end of General
|
||||
@@ -841,7 +889,7 @@ CONFIG_BT_NIMBLE_MAX_CONN_REATTEMPT=3
|
||||
CONFIG_BT_NIMBLE_HS_PVCY=y
|
||||
# CONFIG_BT_NIMBLE_HOST_ALLOW_CONNECT_WITH_SCAN is not set
|
||||
# CONFIG_BT_NIMBLE_HOST_QUEUE_CONG_CHECK is not set
|
||||
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=3
|
||||
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4
|
||||
CONFIG_BT_NIMBLE_MAX_CCCDS=8
|
||||
CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS=y
|
||||
CONFIG_BT_NIMBLE_HS_STOP_TIMEOUT_MS=2000
|
||||
@@ -1004,10 +1052,10 @@ CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23
|
||||
# CONFIG_BT_NIMBLE_MEM_DEBUG is not set
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_NONE is not set
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR is not set
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING is not set
|
||||
CONFIG_BT_NIMBLE_LOG_LEVEL_INFO=y
|
||||
CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING=y
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_INFO is not set
|
||||
# CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG is not set
|
||||
CONFIG_BT_NIMBLE_LOG_LEVEL=1
|
||||
CONFIG_BT_NIMBLE_LOG_LEVEL=2
|
||||
CONFIG_BT_NIMBLE_PRINT_ERR_NAME=y
|
||||
# CONFIG_BT_NIMBLE_DEBUG is not set
|
||||
# CONFIG_BT_NIMBLE_TEST_THROUGHPUT_TEST is not set
|
||||
@@ -1036,9 +1084,9 @@ CONFIG_BT_CTRL_MODE_EFF=1
|
||||
CONFIG_BT_CTRL_BLE_MAX_ACT=6
|
||||
CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=6
|
||||
CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0
|
||||
CONFIG_BT_CTRL_PINNED_TO_CORE_0=y
|
||||
# CONFIG_BT_CTRL_PINNED_TO_CORE_1 is not set
|
||||
CONFIG_BT_CTRL_PINNED_TO_CORE=0
|
||||
# CONFIG_BT_CTRL_PINNED_TO_CORE_0 is not set
|
||||
CONFIG_BT_CTRL_PINNED_TO_CORE_1=y
|
||||
CONFIG_BT_CTRL_PINNED_TO_CORE=1
|
||||
CONFIG_BT_CTRL_HCI_MODE_VHCI=y
|
||||
# CONFIG_BT_CTRL_HCI_MODE_UART_H4 is not set
|
||||
CONFIG_BT_CTRL_HCI_TL=1
|
||||
@@ -1301,15 +1349,15 @@ CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
|
||||
#
|
||||
# ESP-Driver:GPIO Configurations
|
||||
#
|
||||
# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set
|
||||
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
|
||||
# end of ESP-Driver:GPIO Configurations
|
||||
|
||||
#
|
||||
# ESP-Driver:GPTimer Configurations
|
||||
#
|
||||
CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
|
||||
# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set
|
||||
# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set
|
||||
CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=y
|
||||
CONFIG_GPTIMER_ISR_CACHE_SAFE=y
|
||||
CONFIG_GPTIMER_OBJ_CACHE_SAFE=y
|
||||
# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set
|
||||
# end of ESP-Driver:GPTimer Configurations
|
||||
@@ -1642,7 +1690,7 @@ CONFIG_ESP_NETIF_TCPIP_LWIP=y
|
||||
# CONFIG_ESP_NETIF_LOOPBACK is not set
|
||||
CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y
|
||||
CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC=y
|
||||
# CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS is not set
|
||||
CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS=y
|
||||
# CONFIG_ESP_NETIF_L2_TAP is not set
|
||||
# CONFIG_ESP_NETIF_BRIDGE_EN is not set
|
||||
# CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF is not set
|
||||
@@ -1745,9 +1793,9 @@ CONFIG_ESP_ROM_PRINT_IN_IRAM=y
|
||||
# ESP System Settings
|
||||
#
|
||||
# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
|
||||
# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160
|
||||
# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160 is not set
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240
|
||||
|
||||
#
|
||||
# Cache config
|
||||
@@ -1793,7 +1841,7 @@ CONFIG_ESP_SYSTEM_IN_IRAM=y
|
||||
CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y
|
||||
# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set
|
||||
# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set
|
||||
CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0
|
||||
CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=2
|
||||
CONFIG_ESP_SYSTEM_RTC_FAST_MEM_AS_HEAP_DEPCHECK=y
|
||||
CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y
|
||||
|
||||
@@ -1873,9 +1921,9 @@ CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
|
||||
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y
|
||||
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1
|
||||
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32
|
||||
CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y
|
||||
# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set
|
||||
CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0
|
||||
# CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER is not set
|
||||
CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER=y
|
||||
CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=1
|
||||
CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5
|
||||
# CONFIG_ESP_WIFI_CSI_ENABLED is not set
|
||||
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
|
||||
@@ -1883,8 +1931,8 @@ CONFIG_ESP_WIFI_TX_BA_WIN=6
|
||||
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP_WIFI_RX_BA_WIN=6
|
||||
CONFIG_ESP_WIFI_NVS_ENABLED=y
|
||||
CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y
|
||||
# CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1 is not set
|
||||
# CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0 is not set
|
||||
CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1=y
|
||||
CONFIG_ESP_WIFI_SOFTAP_BEACON_MAX_LEN=752
|
||||
CONFIG_ESP_WIFI_MGMT_SBUF_NUM=32
|
||||
CONFIG_ESP_WIFI_IRAM_OPT=y
|
||||
@@ -2001,12 +2049,12 @@ CONFIG_FATFS_DONT_TRUST_LAST_ALLOC=0
|
||||
#
|
||||
# CONFIG_FREERTOS_SMP is not set
|
||||
# CONFIG_FREERTOS_UNICORE is not set
|
||||
CONFIG_FREERTOS_HZ=100
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set
|
||||
# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set
|
||||
CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y
|
||||
CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1
|
||||
CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536
|
||||
CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=2048
|
||||
# CONFIG_FREERTOS_USE_IDLE_HOOK is not set
|
||||
# CONFIG_FREERTOS_USE_TICK_HOOK is not set
|
||||
CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
@@ -2771,21 +2819,25 @@ CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
|
||||
#
|
||||
CONFIG_DALI_PHY_COUNT=16
|
||||
CONFIG_DALI_DEFAULT_BAUDRATE=1200
|
||||
CONFIG_DALI_TIMER_RESOLUTION_HZ=3000000
|
||||
CONFIG_DALI_TIMER_RESOLUTION_HZ=3636363
|
||||
CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US=0
|
||||
CONFIG_DALI_TX_STOP_CONDITION_US=0
|
||||
CONFIG_DALI_RX_STOP_CONDITION_US=0
|
||||
CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS=25
|
||||
CONFIG_DALI_DOUBLE_SEND_DELAY_MS=12
|
||||
CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS=30
|
||||
CONFIG_DALI_DOUBLE_SEND_DELAY_MS=20
|
||||
CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS=20
|
||||
CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS=10
|
||||
CONFIG_DALI_FORWARD_MAX_WAIT_MS=50
|
||||
CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS=10
|
||||
CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS=500
|
||||
CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS=3000
|
||||
# CONFIG_DALI_LOG_LEVEL_NONE is not set
|
||||
# CONFIG_DALI_LOG_LEVEL_ERROR is not set
|
||||
# CONFIG_DALI_LOG_LEVEL_WARN is not set
|
||||
# CONFIG_DALI_LOG_LEVEL_INFO is not set
|
||||
# CONFIG_DALI_LOG_LEVEL_DEBUG is not set
|
||||
CONFIG_DALI_LOG_LEVEL_VERBOSE=y
|
||||
CONFIG_DALI_LOG_LEVEL=5
|
||||
CONFIG_DALI_LOG_LEVEL_DEBUG=y
|
||||
# CONFIG_DALI_LOG_LEVEL_VERBOSE is not set
|
||||
CONFIG_DALI_LOG_LEVEL=4
|
||||
CONFIG_DALI_TX_ACTIVE_LOW=y
|
||||
# CONFIG_DALI_TX_ACTIVE_HIGH is not set
|
||||
CONFIG_DALI_RX_ACTIVE_LOW=y
|
||||
@@ -2795,7 +2847,7 @@ CONFIG_DALI_TX_QUEUE_LEN=4
|
||||
CONFIG_DALI_TX_REPLY_QUEUE_LEN=4
|
||||
CONFIG_DALI_RX_QUEUE_LEN=50
|
||||
CONFIG_DALI_DEBUG_QUEUE_LEN=100
|
||||
# CONFIG_DALI_ENABLE_DEBUG_TASK is not set
|
||||
CONFIG_DALI_ENABLE_DEBUG_TASK=y
|
||||
CONFIG_DALI_DALI_TASK_STACK_SIZE=8192
|
||||
CONFIG_DALI_DALI_TASK_PRIORITY=2
|
||||
CONFIG_DALI_DEBUG_TASK_STACK_SIZE=2048
|
||||
@@ -2818,10 +2870,12 @@ CONFIG_MQTT_TRANSPORT_SSL=y
|
||||
CONFIG_MQTT_TRANSPORT_WEBSOCKET=y
|
||||
CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y
|
||||
# CONFIG_MQTT_MSG_ID_INCREMENTAL is not set
|
||||
# CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set
|
||||
CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED=y
|
||||
# CONFIG_MQTT_REPORT_DELETED_MESSAGES is not set
|
||||
# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set
|
||||
# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set
|
||||
CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED=y
|
||||
# CONFIG_MQTT_USE_CORE_0 is not set
|
||||
CONFIG_MQTT_USE_CORE_1=y
|
||||
# CONFIG_MQTT_CUSTOM_OUTBOX is not set
|
||||
# end of ESP-MQTT Configurations
|
||||
# end of Component config
|
||||
|
||||
+42
-8
@@ -17,11 +17,11 @@ config DALI_DEFAULT_BAUDRATE
|
||||
config DALI_TIMER_RESOLUTION_HZ
|
||||
int "Native DALI timer resolution Hz"
|
||||
range 1000000 8000000
|
||||
default 3000000
|
||||
default 3636363
|
||||
help
|
||||
GPTimer resolution for native DALI Manchester timing. The default 3 MHz
|
||||
allows a 1200 bps half-bit period of 416.67 us to be represented as
|
||||
1250 timer ticks.
|
||||
GPTimer resolution for native DALI Manchester timing. The default
|
||||
3.636363 MHz timing matches the gateway's tuned 1200 bps behavior for
|
||||
older control gear that needs more recovery margin between commands.
|
||||
|
||||
config DALI_CUSTOM_HALF_BIT_TIME_X100_US
|
||||
int "Custom native DALI half-bit time x100 us"
|
||||
@@ -51,7 +51,7 @@ config DALI_RX_STOP_CONDITION_US
|
||||
config DALI_QUERY_RESPONSE_TIMEOUT_MS
|
||||
int "DALI query response timeout ms"
|
||||
range 10 100
|
||||
default 25
|
||||
default 30
|
||||
help
|
||||
Time to wait for a complete backward frame after a forward query has
|
||||
finished transmitting. DALI backward frames start 5.5-10.5 ms after the
|
||||
@@ -61,15 +61,49 @@ config DALI_QUERY_RESPONSE_TIMEOUT_MS
|
||||
config DALI_DOUBLE_SEND_DELAY_MS
|
||||
int "Double-send delay ms"
|
||||
range 0 100
|
||||
default 10
|
||||
default 20
|
||||
help
|
||||
Delay between the two frames sent by dali_send_double(), measured after
|
||||
the first frame has completed. Exposed for development tuning.
|
||||
|
||||
config DALI_FORWARD_ACTIVITY_WAIT_MS
|
||||
int "Forward-frame wait after bus activity ms"
|
||||
range 0 1000
|
||||
default 20
|
||||
help
|
||||
Minimum delay before sending a 2- to 4-byte forward frame after normal
|
||||
bus activity. This is the native queue anti-collision wait.
|
||||
|
||||
config DALI_FORWARD_AFTER_BACKWARD_WAIT_MS
|
||||
int "Forward-frame wait after backward frame ms"
|
||||
range 0 1000
|
||||
default 10
|
||||
help
|
||||
Minimum delay before sending a 2- to 4-byte forward frame after the last
|
||||
valid 1-byte backward frame received by the native bus.
|
||||
|
||||
config DALI_FORWARD_MAX_WAIT_MS
|
||||
int "Forward-frame maximum queue wait ms"
|
||||
range 0 5000
|
||||
default 50
|
||||
help
|
||||
Maximum time the native queue waits for a forward-frame send window. If
|
||||
no valid send window is reached before this timeout, the frame is
|
||||
dropped with an error status.
|
||||
|
||||
config DALI_BACKWARD_IDLE_TIMEOUT_MS
|
||||
int "Backward-frame idle wait timeout ms"
|
||||
range 0 1000
|
||||
default 10
|
||||
help
|
||||
Time a 1-byte backward frame waits for an idle bus before being sent
|
||||
anyway. Backward frame sends are not echo-verified because collisions
|
||||
during addressing responses are valid DALI behavior.
|
||||
|
||||
config DALI_BUS_POWER_CHECK_INTERVAL_MS
|
||||
int "Bus power-down check interval ms"
|
||||
range 10 5000
|
||||
default 100
|
||||
default 500
|
||||
help
|
||||
Interval used to resample the RX pin while the native DALI bus is marked
|
||||
power-down. This lets the HAL recover when the bus was already powered
|
||||
@@ -78,7 +112,7 @@ config DALI_BUS_POWER_CHECK_INTERVAL_MS
|
||||
config DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS
|
||||
int "Legacy bus abnormal report interval ms"
|
||||
range 0 60000
|
||||
default 1000
|
||||
default 3000
|
||||
help
|
||||
Interval for publishing the legacy two-byte FF FD bus-abnormal raw frame
|
||||
while the native DALI bus is power-down. Set to 0 to disable the report.
|
||||
|
||||
@@ -42,7 +42,8 @@ Use `menuconfig` under `DALI Component` to configure:
|
||||
|
||||
- Bus count and default baudrate.
|
||||
- Native timing values for development, including timer resolution, half-bit period,
|
||||
TX/RX stop conditions, query response timeout, and double-send delay.
|
||||
TX/RX stop conditions, query response timeout, double-send delay, and TX queue
|
||||
arbitration waits.
|
||||
- TX/RX active polarity. The native gateway default is TX active low and RX active high.
|
||||
- Native bus power-down polling and legacy `FF FD` bus-abnormal raw-frame reporting intervals.
|
||||
- Native DALI HAL log level for the `dali_hal` ESP-IDF log tag.
|
||||
@@ -52,15 +53,22 @@ Use `menuconfig` under `DALI Component` to configure:
|
||||
|
||||
The native bus monitor uses `CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS` to resample RX while
|
||||
power-down and `CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS` to publish legacy `FF FD`
|
||||
raw frames while down. The report interval defaults to 1000 ms; set it to 0 to disable the
|
||||
raw frames while down. The report interval defaults to 3000 ms; set it to 0 to disable the
|
||||
compatibility report.
|
||||
|
||||
Native timing defaults target standard 1200 bps DALI: a 416.67 us half-bit period is
|
||||
generated by the default 3 MHz timer as 1250 ticks. `CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US`
|
||||
generated by the default 3.636363 MHz timer. `CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US`
|
||||
can override the half-bit period for development; keep it at 0 for baudrate-derived timing.
|
||||
Query no-response timeout defaults to 25 ms, which covers the 5.5-10.5 ms backward-frame
|
||||
Query no-response timeout defaults to 30 ms, which covers the 5.5-10.5 ms backward-frame
|
||||
start window plus the approximately 9.95 ms backward frame duration.
|
||||
|
||||
Native TX queue arbitration uses frame length as the frame type signal. Two- to four-byte
|
||||
forward frames wait up to `CONFIG_DALI_FORWARD_MAX_WAIT_MS` for a valid send window,
|
||||
using `CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS` after normal bus activity and
|
||||
`CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS` after a valid backward frame. One-byte
|
||||
backward frames wait up to `CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS` for idle, then are
|
||||
sent without echo verification because addressing-phase collisions are valid.
|
||||
|
||||
## API Note
|
||||
|
||||
The global TX response queue symbol was renamed:
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#ifndef CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS
|
||||
#define CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS 25
|
||||
#define CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS 30
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_DOUBLE_SEND_DELAY_MS
|
||||
#define CONFIG_DALI_DOUBLE_SEND_DELAY_MS 10
|
||||
#define CONFIG_DALI_DOUBLE_SEND_DELAY_MS 20
|
||||
#endif
|
||||
|
||||
static SemaphoreHandle_t s_dali_core_lock;
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_TIMER_RESOLUTION_HZ
|
||||
#define CONFIG_DALI_TIMER_RESOLUTION_HZ 3000000
|
||||
#define CONFIG_DALI_TIMER_RESOLUTION_HZ 3636363
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US
|
||||
@@ -80,11 +80,27 @@
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS
|
||||
#define CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS 100
|
||||
#define CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS 500
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS
|
||||
#define CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS 1000
|
||||
#define CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS 3000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS
|
||||
#define CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS 20
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS
|
||||
#define CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS 10
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_FORWARD_MAX_WAIT_MS
|
||||
#define CONFIG_DALI_FORWARD_MAX_WAIT_MS 50
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS
|
||||
#define CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS 10
|
||||
#endif
|
||||
|
||||
#define WITHIN_RANGE(x, min, max) ((x) > (min) && (x) < (max))
|
||||
@@ -138,6 +154,11 @@ typedef struct {
|
||||
uint64_t bus_abnormal_report_time;
|
||||
|
||||
Dali_msg_t tx_data;
|
||||
bool bus_activity_seen;
|
||||
uint64_t rx_last_frame_time;
|
||||
uint8_t rx_last_frame_length;
|
||||
uint8_t rx_last_frame_status;
|
||||
volatile bool force_backward_tx;
|
||||
Dali_msg_t rx_data;
|
||||
|
||||
uint8_t tx_half_bit_counter;
|
||||
@@ -381,9 +402,9 @@ static uint32_t tx_completion_timeout_ms(const Dali_msg_t *msg)
|
||||
|
||||
uint64_t frame_us = ((uint64_t)(2U + bits * 2U) * hb) + stop_us;
|
||||
uint32_t frame_ms = (uint32_t)((frame_us + 999U) / 1000U);
|
||||
uint32_t timeout_ms = frame_ms + 30U;
|
||||
if (timeout_ms < 30U) {
|
||||
timeout_ms = 30U;
|
||||
uint32_t timeout_ms = frame_ms + 20U;
|
||||
if (timeout_ms < 20U) {
|
||||
timeout_ms = 20U;
|
||||
}
|
||||
if (timeout_ms > 500U) {
|
||||
timeout_ms = 500U;
|
||||
@@ -391,6 +412,115 @@ static uint32_t tx_completion_timeout_ms(const Dali_msg_t *msg)
|
||||
return timeout_ms;
|
||||
}
|
||||
|
||||
static inline bool IRAM_ATTR is_backward_tx_msg(const Dali_msg_t *msg)
|
||||
{
|
||||
return msg != NULL && msg->length == 8;
|
||||
}
|
||||
|
||||
static bool is_forward_tx_msg(const Dali_msg_t *msg)
|
||||
{
|
||||
return msg != NULL && msg->length >= 16 && msg->length <= 32 && (msg->length % 8) == 0;
|
||||
}
|
||||
|
||||
static UBaseType_t queue_waiting(QueueHandle_t queue);
|
||||
|
||||
static uint64_t bus_last_activity_time_us(const dali_bus_ctx_t *bus)
|
||||
{
|
||||
if (bus == NULL || !bus->bus_activity_seen) {
|
||||
return 0;
|
||||
}
|
||||
return bus->rx_last_edge_time > bus->tx_last_edge_time ? bus->rx_last_edge_time
|
||||
: bus->tx_last_edge_time;
|
||||
}
|
||||
|
||||
static bool bus_idle_for_tx(const dali_bus_ctx_t *bus)
|
||||
{
|
||||
return bus != NULL && bus->bus_state == DALI_BUS_READY && bus->tx_state == TX_STATE_IDLE &&
|
||||
bus->rx_state == RX_STATE_IDLE && bus->rx_level == 1 && queue_waiting(bus->tx_queue) == 0;
|
||||
}
|
||||
|
||||
static bool last_activity_was_backward_frame(const dali_bus_ctx_t *bus, uint64_t last_activity)
|
||||
{
|
||||
return bus != NULL && bus->rx_last_frame_status == DALI_FRAME_OK &&
|
||||
bus->rx_last_frame_length == 8 && bus->rx_last_frame_time != 0 &&
|
||||
bus->rx_last_frame_time >= last_activity;
|
||||
}
|
||||
|
||||
static TickType_t tx_wait_poll_ticks(void)
|
||||
{
|
||||
TickType_t ticks = pdMS_TO_TICKS(1);
|
||||
return ticks == 0 ? 1 : ticks;
|
||||
}
|
||||
|
||||
static bool wait_for_forward_tx_window(dali_bus_ctx_t *bus, const Dali_msg_t *msg)
|
||||
{
|
||||
if (bus == NULL || !is_forward_tx_msg(msg)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t started = esp_timer_get_time();
|
||||
const uint64_t max_wait_us = (uint64_t)CONFIG_DALI_FORWARD_MAX_WAIT_MS * 1000ULL;
|
||||
const TickType_t poll_ticks = tx_wait_poll_ticks();
|
||||
|
||||
while (true) {
|
||||
if (bus->bus_state == DALI_BUS_POWER_DOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t now = esp_timer_get_time();
|
||||
if (bus_idle_for_tx(bus)) {
|
||||
const uint64_t last_activity = bus_last_activity_time_us(bus);
|
||||
if (last_activity == 0) {
|
||||
return true;
|
||||
}
|
||||
const uint32_t wait_ms = last_activity_was_backward_frame(bus, last_activity)
|
||||
? CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS
|
||||
: CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS;
|
||||
if (wait_ms == 0 || (now - last_activity) >= ((uint64_t)wait_ms * 1000ULL)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_wait_us == 0 || (now - started) >= max_wait_us) {
|
||||
return false;
|
||||
}
|
||||
vTaskDelay(poll_ticks);
|
||||
}
|
||||
}
|
||||
|
||||
static bool wait_for_backward_tx_window(dali_bus_ctx_t *bus, const Dali_msg_t *msg,
|
||||
bool *force_start)
|
||||
{
|
||||
if (force_start != NULL) {
|
||||
*force_start = false;
|
||||
}
|
||||
if (bus == NULL || !is_backward_tx_msg(msg)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t started = esp_timer_get_time();
|
||||
const uint64_t timeout_us = (uint64_t)CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS * 1000ULL;
|
||||
const TickType_t poll_ticks = tx_wait_poll_ticks();
|
||||
|
||||
while (true) {
|
||||
if (bus->bus_state == DALI_BUS_POWER_DOWN) {
|
||||
return false;
|
||||
}
|
||||
if (bus_idle_for_tx(bus)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t now = esp_timer_get_time();
|
||||
if (timeout_us == 0 || (now - started) >= timeout_us) {
|
||||
if (force_start != NULL) {
|
||||
*force_start = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
vTaskDelay(poll_ticks);
|
||||
}
|
||||
}
|
||||
|
||||
static UBaseType_t queue_waiting(QueueHandle_t queue)
|
||||
{
|
||||
return queue ? uxQueueMessagesWaiting(queue) : 0;
|
||||
@@ -537,6 +667,16 @@ static inline void publish_rx_frame_from_isr(Dali_msg_t *msg, QueueHandle_t queu
|
||||
}
|
||||
}
|
||||
|
||||
static inline void IRAM_ATTR note_rx_frame_from_isr(dali_bus_ctx_t *bus)
|
||||
{
|
||||
if (bus == NULL) {
|
||||
return;
|
||||
}
|
||||
bus->rx_last_frame_time = bus->rx_last_edge_time;
|
||||
bus->rx_last_frame_length = bus->rx_data.length;
|
||||
bus->rx_last_frame_status = bus->rx_data.status;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR complete_tx_from_isr(dali_bus_ctx_t *bus, BaseType_t *yield)
|
||||
{
|
||||
if (bus == NULL) {
|
||||
@@ -544,7 +684,7 @@ static void IRAM_ATTR complete_tx_from_isr(dali_bus_ctx_t *bus, BaseType_t *yiel
|
||||
}
|
||||
|
||||
bus->tx_data.status = DALI_FRAME_OK;
|
||||
if (bus->tx_reply_queue) {
|
||||
if (!is_backward_tx_msg(&bus->tx_data) && bus->tx_reply_queue) {
|
||||
if (xQueueSendToBackFromISR(bus->tx_reply_queue, &bus->tx_data, yield) != pdTRUE) {
|
||||
Dali_msg_t dropped = {0};
|
||||
xQueueReceiveFromISR(bus->tx_reply_queue, &dropped, yield);
|
||||
@@ -586,6 +726,7 @@ static void IRAM_ATTR rx_gpio_isr_handler(void* arg)
|
||||
|
||||
// rx_level = 1 if and only if DALI bus is really high, idle
|
||||
bus->rx_level = DALI_GET_BUS_LEVEL(bus); // get level of RX pin - not depend on hw: 0 - low, 1 - high
|
||||
bus->bus_activity_seen = true;
|
||||
|
||||
bus->rx_pulse_width = rx_current_edge_time - bus->rx_last_edge_time; // time from last edge
|
||||
bus->rx_tx_delta = rx_current_edge_time - bus->tx_last_edge_time; // time from last edge
|
||||
@@ -693,6 +834,7 @@ static void IRAM_ATTR rx_gpio_isr_handler(void* arg)
|
||||
|
||||
// if collision detected: we are too late after bit was transmitted
|
||||
else if (bus->bus_state == DALI_BUS_TRANSMITTING && bus->tx_state != TX_STATE_STOP &&
|
||||
!is_backward_tx_msg(&bus->tx_data) &&
|
||||
bus->rx_tx_delta > s_timing.collision_txrx_delta)
|
||||
{
|
||||
// we need now to start collision recovery with time break: 101.9.2.4
|
||||
@@ -803,20 +945,40 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
|
||||
}
|
||||
|
||||
// start transmitting
|
||||
else if(bus->bus_state == DALI_BUS_READY && bus->tx_state == TX_STATE_IDLE && bus->tx_queue && xQueueReceiveFromISR(bus->tx_queue, &bus->tx_data, NULL) == pdTRUE)
|
||||
else if(bus->tx_state == TX_STATE_IDLE && bus->tx_queue)
|
||||
{
|
||||
bus->tx_data.status = DALI_FRAME_ERROR; // error status - will be set ok on success
|
||||
bus->bus_state = DALI_BUS_TRANSMITTING; // bus is transmitting
|
||||
bus->tx_state = TX_STATE_START; // start transmitting
|
||||
bus->tx_half_bit_counter = 0;
|
||||
bus->tx_data_bit_counter = 0; // actually sent bits count
|
||||
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
|
||||
DALI_SET_BUS_LOW(bus); // start bit first half
|
||||
Dali_msg_t pending = {0};
|
||||
bool can_start = bus->bus_state == DALI_BUS_READY;
|
||||
bool force_backward = false;
|
||||
if (!can_start && bus->force_backward_tx &&
|
||||
xQueuePeekFromISR(bus->tx_queue, &pending) == pdTRUE) {
|
||||
if (is_backward_tx_msg(&pending) && bus->bus_state != DALI_BUS_POWER_DOWN) {
|
||||
can_start = true;
|
||||
force_backward = true;
|
||||
} else {
|
||||
bus->force_backward_tx = false;
|
||||
}
|
||||
}
|
||||
if (can_start && xQueueReceiveFromISR(bus->tx_queue, &bus->tx_data, &yield) == pdTRUE) {
|
||||
bus->force_backward_tx = false;
|
||||
if (force_backward) {
|
||||
bus->rx_state = RX_STATE_IDLE;
|
||||
}
|
||||
bus->tx_data.status = DALI_FRAME_ERROR; // error status - will be set ok on success
|
||||
bus->bus_activity_seen = true;
|
||||
bus->bus_state = DALI_BUS_TRANSMITTING; // bus is transmitting
|
||||
bus->tx_state = TX_STATE_START; // start transmitting
|
||||
bus->tx_half_bit_counter = 0;
|
||||
bus->tx_data_bit_counter = 0; // actually sent bits count
|
||||
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
|
||||
DALI_SET_BUS_LOW(bus); // start bit first half
|
||||
}
|
||||
}
|
||||
else if(bus->bus_state == DALI_BUS_TRANSMITTING)
|
||||
{
|
||||
uint8_t bus_level = DALI_GET_BUS_LEVEL(bus);
|
||||
if(bus->tx_state != TX_STATE_STOP && tx_delta > s_timing.collision_txrx_delta &&
|
||||
if(bus->tx_state != TX_STATE_STOP && !is_backward_tx_msg(&bus->tx_data) &&
|
||||
tx_delta > s_timing.collision_txrx_delta &&
|
||||
bus_level != DALI_GET_TX_LEVEL(bus)) {
|
||||
bus->rx_level = bus_level;
|
||||
bus->rx_tx_delta = tx_delta;
|
||||
@@ -859,7 +1021,7 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
|
||||
else if(bus->bus_state == DALI_BUS_READY && bus->tx_state > TX_STATE_IDLE)
|
||||
{
|
||||
// we are not transmitting but we have data - reply to queue and let error state in tx_data.status
|
||||
if (bus->tx_reply_queue) {
|
||||
if (bus->tx_reply_queue && !is_backward_tx_msg(&bus->tx_data)) {
|
||||
xQueueSendToBackFromISR(bus->tx_reply_queue, &bus->tx_data, &yield); // send data to queue
|
||||
}
|
||||
bus->tx_state = TX_STATE_IDLE; // clear state
|
||||
@@ -875,6 +1037,7 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
|
||||
// rx_data.status = DALI_FRAME_ERROR; // should be set inside ISR
|
||||
bus->rx_data.length = bus->rx_data_bit_counter; // set length of data
|
||||
// rx_data.data[0] = 0xAA; // debug
|
||||
note_rx_frame_from_isr(bus);
|
||||
publish_rx_frame_from_isr(&bus->rx_data, bus->rx_queue, &yield); // send data to queue
|
||||
}
|
||||
}
|
||||
@@ -886,6 +1049,7 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
|
||||
bus->rx_data.status = DALI_FRAME_OK; // frame is OK
|
||||
bus->rx_data.length = bus->rx_data_bit_counter; // set length of data
|
||||
// rx_data.data[0] = 0xBB; // debug
|
||||
note_rx_frame_from_isr(bus);
|
||||
publish_rx_frame_from_isr(&bus->rx_data, bus->rx_queue, &yield); // send data to queue
|
||||
}
|
||||
}
|
||||
@@ -1117,6 +1281,11 @@ static esp_err_t init_bus(uint8_t bus_id, uint8_t tx_pin, uint8_t rx_pin)
|
||||
bus->tx_last_edge_time = bus->rx_last_edge_time;
|
||||
bus->bus_level_check_time = bus->rx_last_edge_time;
|
||||
bus->bus_abnormal_report_time = 0;
|
||||
bus->bus_activity_seen = false;
|
||||
bus->rx_last_frame_time = 0;
|
||||
bus->rx_last_frame_length = 0;
|
||||
bus->rx_last_frame_status = DALI_FRAME_UNKNOWN;
|
||||
bus->force_backward_tx = false;
|
||||
|
||||
err = ensure_isr_service();
|
||||
if (err != ESP_OK) {
|
||||
@@ -1169,13 +1338,37 @@ static int dali_tx_bus(dali_bus_ctx_t *bus, Dali_msg_t *dali_msg)
|
||||
bus->bus_id, dali_msg->length, dali_msg->data[0], dali_msg->data[1],
|
||||
dali_msg->data[2], dali_msg->data[3], (unsigned long)timeout_ms);
|
||||
|
||||
if(xQueueSendToBack(bus->tx_queue, dali_msg, timeout_ticks) == pdFALSE) {
|
||||
if (is_forward_tx_msg(dali_msg) && !wait_for_forward_tx_window(bus, dali_msg)) {
|
||||
dali_msg->status = DALI_FRAME_ERROR;
|
||||
log_tx_message("native TX wait timeout", bus, dali_msg,
|
||||
CONFIG_DALI_FORWARD_MAX_WAIT_MS);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
bool force_backward = false;
|
||||
if (is_backward_tx_msg(dali_msg) &&
|
||||
!wait_for_backward_tx_window(bus, dali_msg, &force_backward)) {
|
||||
dali_msg->status = DALI_FRAME_ERROR;
|
||||
log_tx_message("native backward TX wait timeout", bus, dali_msg,
|
||||
CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
bus->force_backward_tx = force_backward;
|
||||
if(xQueueSendToBack(bus->tx_queue, dali_msg, 0) == pdFALSE) {
|
||||
bus->force_backward_tx = false;
|
||||
log_tx_message("native TX queue full", bus, dali_msg, timeout_ms);
|
||||
if (bus->tx_state == TX_STATE_IDLE) {
|
||||
xQueueReset(bus->tx_queue);
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (is_backward_tx_msg(dali_msg)) {
|
||||
dali_msg->status = DALI_FRAME_OK;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if(xQueueReceive(bus->tx_reply_queue, dali_msg, timeout_ticks) == pdFALSE) {
|
||||
if (bus->tx_state == TX_STATE_IDLE && queue_waiting(bus->tx_queue) == 0) {
|
||||
*dali_msg = bus->tx_data;
|
||||
|
||||
@@ -68,6 +68,7 @@ struct DaliChannelInfo {
|
||||
uint8_t gateway_id{0};
|
||||
DaliPhyKind phy_kind{DaliPhyKind::kCustom};
|
||||
std::string name;
|
||||
std::optional<uint8_t> native_bus_id;
|
||||
};
|
||||
|
||||
struct DaliRawFrame {
|
||||
@@ -125,6 +126,12 @@ class DaliDomainService {
|
||||
void addRawFrameSink(std::function<void(const DaliRawFrame& frame)> sink);
|
||||
|
||||
bool resetBus(uint8_t gateway_id) const;
|
||||
bool isBusIdle(uint8_t gateway_id, uint32_t quiet_ms) const;
|
||||
void markHostActivity(uint8_t gateway_id) const;
|
||||
void markHostCommandFrame(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const;
|
||||
bool hasRecentHostActivity(uint8_t gateway_id, uint32_t window_ms) const;
|
||||
bool matchesRecentHostCommandFrame(uint8_t gateway_id, uint8_t raw_addr, uint8_t command,
|
||||
uint32_t window_ms) const;
|
||||
bool writeBridgeFrame(uint8_t gateway_id, const uint8_t* data, size_t len) const;
|
||||
std::vector<uint8_t> transactBridgeFrame(uint8_t gateway_id, const uint8_t* data,
|
||||
size_t len) const;
|
||||
@@ -162,6 +169,7 @@ class DaliDomainService {
|
||||
bool on(uint8_t gateway_id, int short_address) const;
|
||||
bool off(uint8_t gateway_id, int short_address) const;
|
||||
bool off(int short_address) const;
|
||||
std::optional<uint8_t> queryActualLevel(uint8_t gateway_id, int short_address) const;
|
||||
std::optional<uint16_t> queryGroupMask(uint8_t gateway_id, int short_address) const;
|
||||
std::optional<uint8_t> querySceneLevel(uint8_t gateway_id, int short_address, int scene) const;
|
||||
std::optional<DaliAddressSettingsSnapshot> queryAddressSettings(uint8_t gateway_id,
|
||||
@@ -181,6 +189,12 @@ class DaliDomainService {
|
||||
|
||||
private:
|
||||
struct DaliChannel;
|
||||
struct RecentHostCommandFrame {
|
||||
uint8_t raw_addr{0};
|
||||
uint8_t command{0};
|
||||
TickType_t tick{0};
|
||||
bool valid{false};
|
||||
};
|
||||
|
||||
DaliChannel* findChannelByGateway(uint8_t gateway_id);
|
||||
const DaliChannel* findChannelByGateway(uint8_t gateway_id) const;
|
||||
@@ -194,10 +208,16 @@ class DaliDomainService {
|
||||
static void RawFrameTaskEntry(void* arg);
|
||||
void rawFrameTaskLoop();
|
||||
void notifyRawFrameSinks(const DaliRawFrame& frame);
|
||||
void markBusActivity(uint8_t gateway_id) const;
|
||||
|
||||
std::vector<std::unique_ptr<DaliChannel>> channels_;
|
||||
std::vector<std::function<void(const DaliRawFrame& frame)>> raw_frame_sinks_;
|
||||
SemaphoreHandle_t raw_frame_sink_lock_{nullptr};
|
||||
mutable SemaphoreHandle_t bus_activity_lock_{nullptr};
|
||||
mutable std::map<uint8_t, TickType_t> last_bus_activity_ticks_;
|
||||
mutable SemaphoreHandle_t host_activity_lock_{nullptr};
|
||||
mutable std::map<uint8_t, TickType_t> last_host_activity_ticks_;
|
||||
mutable std::map<uint8_t, RecentHostCommandFrame> recent_host_command_frames_;
|
||||
TaskHandle_t raw_frame_task_handle_{nullptr};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,22 +1,82 @@
|
||||
#include "dali_domain.hpp"
|
||||
|
||||
#define LOG_LOCAL_LEVEL CONFIG_DALI_LOG_LEVEL
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "dali.h"
|
||||
#include "dali_hal.h"
|
||||
#include "dali.hpp"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#ifndef CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS
|
||||
#define CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS 25
|
||||
#endif
|
||||
|
||||
static const char *TAG = "dali_domain";
|
||||
|
||||
namespace gateway {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kTag = "dali_domain";
|
||||
constexpr size_t kSerialRxPacketMaxBytes = 8;
|
||||
constexpr UBaseType_t kSerialRxQueueDepth = 8;
|
||||
constexpr uint32_t kHardwareQueryRawPostSuppressMs = 10;
|
||||
|
||||
portMUX_TYPE s_query_raw_suppress_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
uint8_t s_query_raw_suppress_inflight[DALI_PHY_COUNT] = {};
|
||||
TickType_t s_query_raw_suppress_post_until[DALI_PHY_COUNT] = {};
|
||||
|
||||
void BeginHardwareQueryRawSuppress(uint8_t bus_id) {
|
||||
if (bus_id >= DALI_PHY_COUNT) {
|
||||
return;
|
||||
}
|
||||
portENTER_CRITICAL(&s_query_raw_suppress_lock);
|
||||
if (s_query_raw_suppress_inflight[bus_id] < UINT8_MAX) {
|
||||
++s_query_raw_suppress_inflight[bus_id];
|
||||
}
|
||||
s_query_raw_suppress_post_until[bus_id] = 0;
|
||||
portEXIT_CRITICAL(&s_query_raw_suppress_lock);
|
||||
}
|
||||
|
||||
bool TakeHardwareQueryRawSuppress(uint8_t bus_id) {
|
||||
if (bus_id >= DALI_PHY_COUNT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool suppress = false;
|
||||
const TickType_t now = xTaskGetTickCount();
|
||||
portENTER_CRITICAL(&s_query_raw_suppress_lock);
|
||||
const TickType_t post_until = s_query_raw_suppress_post_until[bus_id];
|
||||
if (s_query_raw_suppress_inflight[bus_id] > 0) {
|
||||
suppress = true;
|
||||
} else if (post_until != 0 && now <= post_until) {
|
||||
suppress = true;
|
||||
} else if (post_until != 0) {
|
||||
s_query_raw_suppress_post_until[bus_id] = 0;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_query_raw_suppress_lock);
|
||||
return suppress;
|
||||
}
|
||||
|
||||
void ClearHardwareQueryRawSuppress(uint8_t bus_id) {
|
||||
if (bus_id >= DALI_PHY_COUNT) {
|
||||
return;
|
||||
}
|
||||
const TickType_t post_until = xTaskGetTickCount() +
|
||||
pdMS_TO_TICKS(kHardwareQueryRawPostSuppressMs);
|
||||
portENTER_CRITICAL(&s_query_raw_suppress_lock);
|
||||
if (s_query_raw_suppress_inflight[bus_id] > 0) {
|
||||
--s_query_raw_suppress_inflight[bus_id];
|
||||
}
|
||||
if (s_query_raw_suppress_inflight[bus_id] == 0) {
|
||||
s_query_raw_suppress_post_until[bus_id] = post_until;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_query_raw_suppress_lock);
|
||||
}
|
||||
|
||||
DaliDomainSnapshot MakeSnapshot(uint8_t gateway_id, int address, const char* kind) {
|
||||
DaliDomainSnapshot snapshot;
|
||||
@@ -68,6 +128,16 @@ std::vector<uint8_t> LegacyQueryResponse(uint8_t status, uint8_t value = 0x00) {
|
||||
return {status, value};
|
||||
}
|
||||
|
||||
void LogQueryRxPacket(const char* transport, int id, const std::vector<uint8_t>& packet,
|
||||
const char* note = nullptr) {
|
||||
const unsigned first = packet.size() > 0 ? packet[0] : 0;
|
||||
const unsigned second = packet.size() > 1 ? packet[1] : 0;
|
||||
ESP_LOGD(TAG, "%s query rx packet id=%d len=%u data=%02x %02x%s%s",
|
||||
transport == nullptr ? "dali" : transport, id,
|
||||
static_cast<unsigned>(packet.size()), first, second,
|
||||
note == nullptr ? "" : " note=", note == nullptr ? "" : note);
|
||||
}
|
||||
|
||||
bool SendHardwareFrame(uint8_t bus_id, const uint8_t* data, size_t len) {
|
||||
if (data == nullptr || len != 3) {
|
||||
return false;
|
||||
@@ -82,9 +152,11 @@ bool SendHardwareFrame(uint8_t bus_id, const uint8_t* data, size_t len) {
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x10:
|
||||
ESP_LOGD(TAG, "sending hardware frame for bus=%u data=%02x %02x", bus_id, data[1], data[2]);
|
||||
dali_send(&tx);
|
||||
return true;
|
||||
case 0x11:
|
||||
ESP_LOGD(TAG, "sending extended frame for bus=%u data=%02x %02x", bus_id, data[1], data[2]);
|
||||
dali_send_double(&tx);
|
||||
return true;
|
||||
default:
|
||||
@@ -97,12 +169,17 @@ std::vector<uint8_t> TransactHardwareFrame(uint8_t bus_id, const uint8_t* data,
|
||||
return {};
|
||||
}
|
||||
if (len != 3) {
|
||||
return len > 0 && data[0] == 0x12 ? LegacyQueryResponse(0xFD) : std::vector<uint8_t>{};
|
||||
if (len > 0 && data[0] == 0x12) {
|
||||
const auto packet = LegacyQueryResponse(0xFD);
|
||||
LogQueryRxPacket("hardware", bus_id, packet, "invalid-query-len");
|
||||
return packet;
|
||||
}
|
||||
return std::vector<uint8_t>{};
|
||||
}
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x00:
|
||||
return {0xFF};
|
||||
ESP_LOGD(TAG, "received reset for bus=%u", bus_id);
|
||||
case 0x01:
|
||||
return {1};
|
||||
case 0x10:
|
||||
@@ -110,16 +187,31 @@ std::vector<uint8_t> TransactHardwareFrame(uint8_t bus_id, const uint8_t* data,
|
||||
return SendHardwareFrame(bus_id, data, len) ? std::vector<uint8_t>{0xFF}
|
||||
: std::vector<uint8_t>{0xFD};
|
||||
case 0x12: {
|
||||
ESP_LOGD(TAG, "received hardware query frame for bus=%u data=%02x %02x", bus_id, data[1],
|
||||
data[2]);
|
||||
Dali_msg_t tx = dali_msg_new(data[1], data[2]);
|
||||
tx.id = bus_id;
|
||||
Dali_msg_t rx = {};
|
||||
BeginHardwareQueryRawSuppress(bus_id);
|
||||
if (dali_query(&tx, &rx) == pdTRUE) {
|
||||
ClearHardwareQueryRawSuppress(bus_id);
|
||||
if (rx.status != DALI_FRAME_OK || rx.length != 8) {
|
||||
return LegacyQueryResponse(0xFD);
|
||||
ESP_LOGW(TAG, "hardware query response for bus=%u has invalid status or length", bus_id);
|
||||
const auto packet = LegacyQueryResponse(0xFD);
|
||||
LogQueryRxPacket("hardware", bus_id, packet, "invalid-status-or-length");
|
||||
return packet;
|
||||
}
|
||||
return {0xFF, rx.data[0]};
|
||||
ESP_LOGD(TAG, "got hardware query response for bus=%u status=%u len=%u data=%02x %02x "
|
||||
"%02x %02x",
|
||||
bus_id, rx.status, rx.length, rx.data[0], rx.data[1], rx.data[2], rx.data[3]);
|
||||
const std::vector<uint8_t> packet{0xFF, rx.data[0]};
|
||||
LogQueryRxPacket("hardware", bus_id, packet, "ok");
|
||||
return packet;
|
||||
}
|
||||
return LegacyQueryResponse(0xFE);
|
||||
ClearHardwareQueryRawSuppress(bus_id);
|
||||
const auto packet = LegacyQueryResponse(0xFE);
|
||||
LogQueryRxPacket("hardware", bus_id, packet, "no-response");
|
||||
return packet;
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
@@ -162,16 +254,22 @@ std::vector<uint8_t> TransactSerialFrame(int uart_port, QueueHandle_t queue,
|
||||
uint32_t query_timeout_ms, const uint8_t* data,
|
||||
size_t len) {
|
||||
if (data == nullptr || len == 0) {
|
||||
return LegacyQueryResponse(0xFD);
|
||||
const auto packet = LegacyQueryResponse(0xFD);
|
||||
LogQueryRxPacket("serial", uart_port, packet, "empty-query");
|
||||
return packet;
|
||||
}
|
||||
if (data[0] == 0x12 && len != 3) {
|
||||
return LegacyQueryResponse(0xFD);
|
||||
const auto packet = LegacyQueryResponse(0xFD);
|
||||
LogQueryRxPacket("serial", uart_port, packet, "invalid-query-len");
|
||||
return packet;
|
||||
}
|
||||
if (data != nullptr && len > 0 && data[0] == 0x12) {
|
||||
DrainSerialQueue(queue);
|
||||
}
|
||||
if (!WriteSerialFrame(uart_port, data, len)) {
|
||||
return LegacyQueryResponse(0xFD);
|
||||
const auto packet = LegacyQueryResponse(0xFD);
|
||||
LogQueryRxPacket("serial", uart_port, packet, "write-failed");
|
||||
return packet;
|
||||
}
|
||||
if (data[0] != 0x12) {
|
||||
return {0xFF};
|
||||
@@ -187,12 +285,17 @@ std::vector<uint8_t> TransactSerialFrame(int uart_port, QueueHandle_t queue,
|
||||
break;
|
||||
}
|
||||
auto response = PacketToVector(packet, 2);
|
||||
LogQueryRxPacket("serial", uart_port, response, "raw");
|
||||
if (!response.empty() &&
|
||||
(response[0] == 0xFF || response[0] == 0xFE || response[0] == 0xFD)) {
|
||||
return LegacyQueryResponse(response[0], response.size() > 1 ? response[1] : 0x00);
|
||||
const auto packet = LegacyQueryResponse(response[0], response.size() > 1 ? response[1] : 0x00);
|
||||
LogQueryRxPacket("serial", uart_port, packet, "accepted");
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
return LegacyQueryResponse(0xFE);
|
||||
const auto packet = LegacyQueryResponse(0xFE);
|
||||
LogQueryRxPacket("serial", uart_port, packet, "timeout");
|
||||
return packet;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -211,9 +314,26 @@ struct DaliDomainService::DaliChannel {
|
||||
};
|
||||
|
||||
DaliDomainService::DaliDomainService()
|
||||
: raw_frame_sink_lock_(xSemaphoreCreateMutex()) {}
|
||||
: raw_frame_sink_lock_(xSemaphoreCreateMutex()),
|
||||
bus_activity_lock_(xSemaphoreCreateMutex()),
|
||||
host_activity_lock_(xSemaphoreCreateMutex()) {
|
||||
esp_log_level_set(TAG, (esp_log_level_t)CONFIG_DALI_LOG_LEVEL);
|
||||
}
|
||||
|
||||
DaliDomainService::~DaliDomainService() = default;
|
||||
DaliDomainService::~DaliDomainService() {
|
||||
if (raw_frame_sink_lock_ != nullptr) {
|
||||
vSemaphoreDelete(raw_frame_sink_lock_);
|
||||
raw_frame_sink_lock_ = nullptr;
|
||||
}
|
||||
if (bus_activity_lock_ != nullptr) {
|
||||
vSemaphoreDelete(bus_activity_lock_);
|
||||
bus_activity_lock_ = nullptr;
|
||||
}
|
||||
if (host_activity_lock_ != nullptr) {
|
||||
vSemaphoreDelete(host_activity_lock_);
|
||||
host_activity_lock_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool DaliDomainService::bindTransport(const DaliChannelConfig& config, DaliTransportHooks hooks) {
|
||||
if (!hooks.send) {
|
||||
@@ -241,14 +361,14 @@ bool DaliDomainService::bindTransport(const DaliChannelConfig& config, DaliTrans
|
||||
esp_err_t DaliDomainService::bindHardwareBus(const DaliHardwareBusConfig& config) {
|
||||
esp_err_t err = dali_hal_set_baudrate(config.baudrate);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to set baudrate=%lu: %s", static_cast<unsigned long>(config.baudrate),
|
||||
ESP_LOGE(TAG, "failed to set baudrate=%lu: %s", static_cast<unsigned long>(config.baudrate),
|
||||
esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = dali_hal_init(config.bus_id, config.tx_pin, config.rx_pin);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to init bus=%u tx=%u rx=%u: %s", config.bus_id, config.tx_pin,
|
||||
ESP_LOGE(TAG, "failed to init bus=%u tx=%u rx=%u: %s", config.bus_id, config.tx_pin,
|
||||
config.rx_pin, esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
@@ -274,9 +394,9 @@ esp_err_t DaliDomainService::bindHardwareBus(const DaliHardwareBusConfig& config
|
||||
}
|
||||
err = startRawFrameTask();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(kTag, "failed to start raw frame task: %s", esp_err_to_name(err));
|
||||
ESP_LOGW(TAG, "failed to start raw frame task: %s", esp_err_to_name(err));
|
||||
}
|
||||
ESP_LOGI(kTag, "bound channel=%u gateway=%u hardware bus=%u tx=%u rx=%u baudrate=%lu",
|
||||
ESP_LOGI(TAG, "bound channel=%u gateway=%u hardware bus=%u tx=%u rx=%u baudrate=%lu",
|
||||
config.channel_index, config.gateway_id, config.bus_id, config.tx_pin, config.rx_pin,
|
||||
static_cast<unsigned long>(config.baudrate));
|
||||
return ESP_OK;
|
||||
@@ -287,7 +407,7 @@ esp_err_t DaliDomainService::bindSerialBus(const DaliSerialBusConfig& config) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (hasSerialPort(config.uart_port)) {
|
||||
ESP_LOGE(kTag, "uart%d is already assigned to another DALI channel", config.uart_port);
|
||||
ESP_LOGE(TAG, "uart%d is already assigned to another DALI channel", config.uart_port);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -303,27 +423,27 @@ esp_err_t DaliDomainService::bindSerialBus(const DaliSerialBusConfig& config) {
|
||||
auto uart = static_cast<uart_port_t>(config.uart_port);
|
||||
esp_err_t err = uart_param_config(uart, &uart_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to configure uart%d: %s", config.uart_port, esp_err_to_name(err));
|
||||
ESP_LOGE(TAG, "failed to configure uart%d: %s", config.uart_port, esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
err = uart_set_pin(uart, config.tx_pin < 0 ? UART_PIN_NO_CHANGE : config.tx_pin,
|
||||
config.rx_pin < 0 ? UART_PIN_NO_CHANGE : config.rx_pin,
|
||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to set uart%d pins tx=%d rx=%d: %s", config.uart_port,
|
||||
ESP_LOGE(TAG, "failed to set uart%d pins tx=%d rx=%d: %s", config.uart_port,
|
||||
config.tx_pin, config.rx_pin, esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
err = uart_driver_install(uart, config.rx_buffer_size, config.tx_buffer_size, 0, nullptr, 0);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to install uart%d driver: %s", config.uart_port, esp_err_to_name(err));
|
||||
ESP_LOGE(TAG, "failed to install uart%d driver: %s", config.uart_port, esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
uart_flush_input(uart);
|
||||
|
||||
QueueHandle_t serial_rx_queue = xQueueCreate(kSerialRxQueueDepth, sizeof(SerialRxPacket));
|
||||
if (serial_rx_queue == nullptr) {
|
||||
ESP_LOGE(kTag, "failed to create uart%d RX queue", config.uart_port);
|
||||
ESP_LOGE(TAG, "failed to create uart%d RX queue", config.uart_port);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
@@ -353,12 +473,12 @@ esp_err_t DaliDomainService::bindSerialBus(const DaliSerialBusConfig& config) {
|
||||
channel->serial_rx_queue = serial_rx_queue;
|
||||
err = startSerialRxTask(*channel);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to start uart%d RX task: %s", config.uart_port,
|
||||
ESP_LOGE(TAG, "failed to start uart%d RX task: %s", config.uart_port,
|
||||
esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(kTag, "bound channel=%u gateway=%u serial uart%d tx=%d rx=%d baudrate=%lu",
|
||||
ESP_LOGI(TAG, "bound channel=%u gateway=%u serial uart%d tx=%d rx=%d baudrate=%lu",
|
||||
config.channel_index, config.gateway_id, config.uart_port, config.tx_pin, config.rx_pin,
|
||||
static_cast<unsigned long>(config.baudrate));
|
||||
return ESP_OK;
|
||||
@@ -403,8 +523,12 @@ std::vector<DaliChannelInfo> DaliDomainService::channelInfo() const {
|
||||
std::vector<DaliChannelInfo> info;
|
||||
info.reserve(channels_.size());
|
||||
for (const auto& channel : channels_) {
|
||||
info.push_back(DaliChannelInfo{channel->config.channel_index, channel->config.gateway_id,
|
||||
channel->phy_kind, channel->config.name});
|
||||
DaliChannelInfo item{channel->config.channel_index, channel->config.gateway_id,
|
||||
channel->phy_kind, channel->config.name};
|
||||
if (channel->hardware_bus.has_value()) {
|
||||
item.native_bus_id = channel->hardware_bus->bus_id;
|
||||
}
|
||||
info.push_back(std::move(item));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
@@ -424,12 +548,112 @@ void DaliDomainService::addRawFrameSink(std::function<void(const DaliRawFrame& f
|
||||
|
||||
bool DaliDomainService::resetBus(uint8_t gateway_id) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->comm != nullptr && channel->comm->resetBus();
|
||||
if (channel == nullptr || channel->comm == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->comm->resetBus();
|
||||
}
|
||||
|
||||
bool DaliDomainService::isBusIdle(uint8_t gateway_id, uint32_t quiet_ms) const {
|
||||
TickType_t last_activity = 0;
|
||||
if (bus_activity_lock_ != nullptr) {
|
||||
xSemaphoreTake(bus_activity_lock_, portMAX_DELAY);
|
||||
}
|
||||
if (const auto it = last_bus_activity_ticks_.find(gateway_id);
|
||||
it != last_bus_activity_ticks_.end()) {
|
||||
last_activity = it->second;
|
||||
}
|
||||
if (bus_activity_lock_ != nullptr) {
|
||||
xSemaphoreGive(bus_activity_lock_);
|
||||
}
|
||||
if (last_activity == 0) {
|
||||
return true;
|
||||
}
|
||||
return (xTaskGetTickCount() - last_activity) >= pdMS_TO_TICKS(quiet_ms);
|
||||
}
|
||||
|
||||
void DaliDomainService::markHostActivity(uint8_t gateway_id) const {
|
||||
if (host_activity_lock_ != nullptr) {
|
||||
xSemaphoreTake(host_activity_lock_, portMAX_DELAY);
|
||||
}
|
||||
last_host_activity_ticks_[gateway_id] = xTaskGetTickCount();
|
||||
if (host_activity_lock_ != nullptr) {
|
||||
xSemaphoreGive(host_activity_lock_);
|
||||
}
|
||||
}
|
||||
|
||||
void DaliDomainService::markHostCommandFrame(uint8_t gateway_id, uint8_t raw_addr,
|
||||
uint8_t command) const {
|
||||
const TickType_t now = xTaskGetTickCount();
|
||||
if (host_activity_lock_ != nullptr) {
|
||||
xSemaphoreTake(host_activity_lock_, portMAX_DELAY);
|
||||
}
|
||||
last_host_activity_ticks_[gateway_id] = now;
|
||||
recent_host_command_frames_[gateway_id] = RecentHostCommandFrame{raw_addr, command, now, true};
|
||||
if (host_activity_lock_ != nullptr) {
|
||||
xSemaphoreGive(host_activity_lock_);
|
||||
}
|
||||
}
|
||||
|
||||
bool DaliDomainService::hasRecentHostActivity(uint8_t gateway_id, uint32_t window_ms) const {
|
||||
if (window_ms == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TickType_t last_activity = 0;
|
||||
if (host_activity_lock_ != nullptr) {
|
||||
xSemaphoreTake(host_activity_lock_, portMAX_DELAY);
|
||||
}
|
||||
if (const auto it = last_host_activity_ticks_.find(gateway_id);
|
||||
it != last_host_activity_ticks_.end()) {
|
||||
last_activity = it->second;
|
||||
}
|
||||
if (host_activity_lock_ != nullptr) {
|
||||
xSemaphoreGive(host_activity_lock_);
|
||||
}
|
||||
if (last_activity == 0) {
|
||||
return false;
|
||||
}
|
||||
return (xTaskGetTickCount() - last_activity) < pdMS_TO_TICKS(window_ms);
|
||||
}
|
||||
|
||||
bool DaliDomainService::matchesRecentHostCommandFrame(uint8_t gateway_id, uint8_t raw_addr,
|
||||
uint8_t command,
|
||||
uint32_t window_ms) const {
|
||||
if (window_ms == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RecentHostCommandFrame frame;
|
||||
if (host_activity_lock_ != nullptr) {
|
||||
xSemaphoreTake(host_activity_lock_, portMAX_DELAY);
|
||||
}
|
||||
if (const auto it = recent_host_command_frames_.find(gateway_id);
|
||||
it != recent_host_command_frames_.end()) {
|
||||
frame = it->second;
|
||||
}
|
||||
if (host_activity_lock_ != nullptr) {
|
||||
xSemaphoreGive(host_activity_lock_);
|
||||
}
|
||||
if (!frame.valid || frame.raw_addr != raw_addr || frame.command != command) {
|
||||
return false;
|
||||
}
|
||||
return (xTaskGetTickCount() - frame.tick) < pdMS_TO_TICKS(window_ms);
|
||||
}
|
||||
|
||||
bool DaliDomainService::writeBridgeFrame(uint8_t gateway_id, const uint8_t* data, size_t len) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->hooks.send && channel->hooks.send(data, len);
|
||||
if (channel == nullptr || !channel->hooks.send) {
|
||||
return false;
|
||||
}
|
||||
if (data != nullptr && len == 3 && (data[0] == 0x10 || data[0] == 0x11)) {
|
||||
markHostCommandFrame(gateway_id, data[1], data[2]);
|
||||
} else {
|
||||
markHostActivity(gateway_id);
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->hooks.send(data, len);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> DaliDomainService::transactBridgeFrame(uint8_t gateway_id,
|
||||
@@ -439,17 +663,33 @@ std::vector<uint8_t> DaliDomainService::transactBridgeFrame(uint8_t gateway_id,
|
||||
if (channel == nullptr || !channel->hooks.transact) {
|
||||
return {};
|
||||
}
|
||||
if (data != nullptr && len == 3 && (data[0] == 0x10 || data[0] == 0x11 || data[0] == 0x12)) {
|
||||
markHostCommandFrame(gateway_id, data[1], data[2]);
|
||||
} else {
|
||||
markHostActivity(gateway_id);
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->hooks.transact(data, len);
|
||||
}
|
||||
|
||||
bool DaliDomainService::sendRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->comm != nullptr && channel->comm->sendRawNew(raw_addr, command);
|
||||
if (channel == nullptr || channel->comm == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markHostCommandFrame(gateway_id, raw_addr, command);
|
||||
markBusActivity(gateway_id);
|
||||
return channel->comm->sendRawNew(raw_addr, command);
|
||||
}
|
||||
|
||||
bool DaliDomainService::sendExtRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->comm != nullptr && channel->comm->sendExtRawNew(raw_addr, command);
|
||||
if (channel == nullptr || channel->comm == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markHostCommandFrame(gateway_id, raw_addr, command);
|
||||
markBusActivity(gateway_id);
|
||||
return channel->comm->sendExtRawNew(raw_addr, command);
|
||||
}
|
||||
|
||||
std::optional<uint8_t> DaliDomainService::queryRaw(uint8_t gateway_id, uint8_t raw_addr,
|
||||
@@ -458,6 +698,8 @@ std::optional<uint8_t> DaliDomainService::queryRaw(uint8_t gateway_id, uint8_t r
|
||||
if (channel == nullptr || channel->comm == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markHostCommandFrame(gateway_id, raw_addr, command);
|
||||
markBusActivity(gateway_id);
|
||||
return channel->comm->queryRawNew(raw_addr, command);
|
||||
}
|
||||
|
||||
@@ -468,6 +710,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::discoverDeviceTypes(
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
const std::vector<int> fallback = fallback_types.empty() ? std::vector<int>{1, 4, 5, 6, 8}
|
||||
: fallback_types;
|
||||
auto discovery = channel->dali->base.discoverDeviceTypes(short_address, fallback,
|
||||
@@ -489,6 +732,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::baseStatusSnapshot(
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
const auto raw_status = channel->dali->base.getStatus(short_address);
|
||||
if (!raw_status.has_value()) {
|
||||
return std::nullopt;
|
||||
@@ -514,6 +758,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt1Snapshot(uint8_t gateway
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
const auto detailed = channel->dali->dt1.getDT1TestStatusDetailed(short_address);
|
||||
if (!detailed.has_value()) {
|
||||
return std::nullopt;
|
||||
@@ -525,6 +770,23 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt1Snapshot(uint8_t gateway
|
||||
PutOptionalInt(snapshot, "emergencyModeRaw", detailed->emergencyMode);
|
||||
PutOptionalInt(snapshot, "featuresRaw", detailed->feature);
|
||||
PutOptionalInt(snapshot, "deviceStatusRaw", detailed->deviceStatus);
|
||||
PutOptionalInt(snapshot, "batteryChargeLevel", detailed->batteryChargeLevel);
|
||||
PutOptionalInt(snapshot, "functionTestDelayTime", detailed->functionTestDelayTime);
|
||||
PutOptionalInt(snapshot, "durationTestDelayTime", detailed->durationTestDelayTime);
|
||||
PutOptionalInt(snapshot, "functionTestIntervalDays", detailed->functionTestIntervalDays);
|
||||
PutOptionalInt(snapshot, "durationTestIntervalWeeks", detailed->durationTestIntervalWeeks);
|
||||
PutOptionalInt(snapshot, "testExecutionTimeoutDays", detailed->testExecutionTimeoutDays);
|
||||
PutOptionalInt(snapshot, "prolongTimeHalfMinutes", detailed->prolongTimeHalfMinutes);
|
||||
PutOptionalInt(snapshot, "durationTestResultMinutes", detailed->durationTestResultMinutes);
|
||||
PutOptionalInt(snapshot, "lampEmergencyTimeHours", detailed->lampEmergencyTimeHours);
|
||||
PutOptionalInt(snapshot, "lampTotalOperationTimeHours", detailed->lampTotalOperationTimeHours);
|
||||
PutOptionalInt(snapshot, "emergencyLevel", detailed->emergencyLevel);
|
||||
PutOptionalInt(snapshot, "emergencyMinLevel", detailed->emergencyMinLevel);
|
||||
PutOptionalInt(snapshot, "emergencyMaxLevel", detailed->emergencyMaxLevel);
|
||||
PutOptionalInt(snapshot, "ratedDurationMinutes", detailed->ratedDurationMinutes);
|
||||
PutOptionalInt(snapshot, "extendedVersion", detailed->extendedVersion);
|
||||
PutOptionalInt(snapshot, "physicalMinLevel", detailed->physicalMinLevel);
|
||||
PutOptionalInt(snapshot, "emergencyDeviceTypeCode", detailed->emergencyDeviceTypeCode);
|
||||
|
||||
snapshot.bools["testInProgress"] = detailed->testInProgress;
|
||||
snapshot.bools["lampFailure"] = detailed->lampFailure;
|
||||
@@ -578,6 +840,8 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt1Snapshot(uint8_t gateway
|
||||
snapshot.bools["hardwiredInhibitSupported"] = features.hardwiredInhibitSupported();
|
||||
snapshot.bools["physicalSelectionSupported"] = features.physicalSelectionSupported();
|
||||
snapshot.bools["relightInRestModeSupported"] = features.relightInRestModeSupported();
|
||||
snapshot.ints["derivedEmergencyDeviceTypeCode"] =
|
||||
features.emergencyDeviceTypeCode(detailed->physicalMinLevel);
|
||||
}
|
||||
if (detailed->deviceStatus.has_value()) {
|
||||
const DaliDT1DeviceStatus status(detailed->deviceStatus.value());
|
||||
@@ -596,6 +860,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt4Snapshot(uint8_t gateway
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
auto snapshot = MakeSnapshot(gateway_id, short_address, "dt4");
|
||||
auto& dt4 = channel->dali->dt4;
|
||||
PutOptionalInt(snapshot, "extendedVersion", dt4.getExtendedVersion(short_address));
|
||||
@@ -678,6 +943,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt5Snapshot(uint8_t gateway
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
auto snapshot = MakeSnapshot(gateway_id, short_address, "dt5");
|
||||
auto& dt5 = channel->dali->dt5;
|
||||
PutOptionalInt(snapshot, "extendedVersion", dt5.getExtendedVersion(short_address));
|
||||
@@ -719,6 +985,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt6Snapshot(uint8_t gateway
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
auto snapshot = MakeSnapshot(gateway_id, short_address, "dt6");
|
||||
auto& dt6 = channel->dali->dt6;
|
||||
PutOptionalInt(snapshot, "extendedVersion", dt6.getExtendedVersion(short_address));
|
||||
@@ -782,6 +1049,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8StatusSnapshot(
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
|
||||
auto snapshot = MakeSnapshot(gateway_id, short_address, "dt8_status");
|
||||
bool has_data = false;
|
||||
@@ -816,6 +1084,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8SceneColorReport(
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
const auto report = channel->dali->dt8.getSceneColorReport(short_address, scene);
|
||||
if (!report.has_value()) {
|
||||
return std::nullopt;
|
||||
@@ -839,6 +1108,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8PowerOnLevelColorReport(
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
const auto report = channel->dali->dt8.getPowerOnLevelColorReport(short_address);
|
||||
if (!report.has_value()) {
|
||||
return std::nullopt;
|
||||
@@ -861,6 +1131,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8SystemFailureLevelColorR
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
const auto report = channel->dali->dt8.getSystemFailureLevelColorReport(short_address);
|
||||
if (!report.has_value()) {
|
||||
return std::nullopt;
|
||||
@@ -883,8 +1154,11 @@ bool DaliDomainService::storeDt8SceneSnapshot(uint8_t gateway_id, int short_addr
|
||||
int color_temperature, int red, int green,
|
||||
int blue) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr &&
|
||||
channel->dali->dt8.storeSceneSnapshot(short_address, scene, brightness,
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->dt8.storeSceneSnapshot(short_address, scene, brightness,
|
||||
ToDaliCppColorMode(color_mode), color_temperature,
|
||||
red, green, blue);
|
||||
}
|
||||
@@ -892,56 +1166,85 @@ bool DaliDomainService::storeDt8SceneSnapshot(uint8_t gateway_id, int short_addr
|
||||
bool DaliDomainService::storeDt8PowerOnLevelSnapshot(uint8_t gateway_id, int short_address,
|
||||
int level) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr &&
|
||||
channel->dali->dt8.storePowerOnLevelSnapshot(short_address, level);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->dt8.storePowerOnLevelSnapshot(short_address, level);
|
||||
}
|
||||
|
||||
bool DaliDomainService::storeDt8SystemFailureLevelSnapshot(uint8_t gateway_id,
|
||||
int short_address, int level) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr &&
|
||||
channel->dali->dt8.storeSystemFailureLevelSnapshot(short_address, level);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->dt8.storeSystemFailureLevelSnapshot(short_address, level);
|
||||
}
|
||||
|
||||
bool DaliDomainService::setBright(uint8_t gateway_id, int short_address, int brightness) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr &&
|
||||
channel->dali->base.setBright(short_address, brightness);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->base.setBright(short_address, brightness);
|
||||
}
|
||||
|
||||
bool DaliDomainService::setColTempRaw(uint8_t gateway_id, int short_address, int mirek) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr &&
|
||||
channel->dali->dt8.setColTempRaw(short_address, mirek);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->dt8.setColTempRaw(short_address, mirek);
|
||||
}
|
||||
|
||||
bool DaliDomainService::setColTemp(uint8_t gateway_id, int short_address, int kelvin) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr &&
|
||||
channel->dali->dt8.setColorTemperature(short_address, kelvin);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->dt8.setColorTemperature(short_address, kelvin);
|
||||
}
|
||||
|
||||
bool DaliDomainService::setColourRaw(uint8_t gateway_id, int raw_addr, int x, int y) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr &&
|
||||
channel->dali->dt8.setColourRaw(raw_addr, x, y);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->dt8.setColourRaw(raw_addr, x, y);
|
||||
}
|
||||
|
||||
bool DaliDomainService::setColourRGB(uint8_t gateway_id, int short_address, int r, int g,
|
||||
int b) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr &&
|
||||
channel->dali->dt8.setColourRGB(short_address, r, g, b);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->dt8.setColourRGB(short_address, r, g, b);
|
||||
}
|
||||
|
||||
bool DaliDomainService::on(uint8_t gateway_id, int short_address) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr && channel->dali->base.on(short_address);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->base.on(short_address);
|
||||
}
|
||||
|
||||
bool DaliDomainService::off(uint8_t gateway_id, int short_address) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr && channel->dali->base.off(short_address);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->base.off(short_address);
|
||||
}
|
||||
|
||||
bool DaliDomainService::off(int short_address) const {
|
||||
@@ -952,6 +1255,20 @@ bool DaliDomainService::off(int short_address) const {
|
||||
return off(channels_.front()->config.gateway_id, short_address);
|
||||
}
|
||||
|
||||
std::optional<uint8_t> DaliDomainService::queryActualLevel(uint8_t gateway_id,
|
||||
int short_address) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
const auto level = channel->dali->base.getBright(short_address);
|
||||
if (!level.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<uint8_t>(std::clamp(*level, 0, 254));
|
||||
}
|
||||
|
||||
std::optional<uint16_t> DaliDomainService::queryGroupMask(uint8_t gateway_id,
|
||||
int short_address) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
@@ -959,6 +1276,8 @@ std::optional<uint16_t> DaliDomainService::queryGroupMask(uint8_t gateway_id,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
markBusActivity(gateway_id);
|
||||
|
||||
const auto group_mask = channel->dali->base.getGroup(short_address);
|
||||
if (!group_mask.has_value()) {
|
||||
return std::nullopt;
|
||||
@@ -974,6 +1293,8 @@ std::optional<uint8_t> DaliDomainService::querySceneLevel(uint8_t gateway_id, in
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
markBusActivity(gateway_id);
|
||||
|
||||
const auto level = channel->dali->base.getScene(short_address, scene);
|
||||
if (!level.has_value()) {
|
||||
return std::nullopt;
|
||||
@@ -989,11 +1310,15 @@ std::optional<DaliAddressSettingsSnapshot> DaliDomainService::queryAddressSettin
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
markBusActivity(gateway_id);
|
||||
|
||||
DaliAddressSettingsSnapshot settings{};
|
||||
|
||||
if (const auto value = channel->dali->base.getPowerOnLevel(short_address); value.has_value()) {
|
||||
settings.power_on_level = static_cast<uint8_t>(*value);
|
||||
const auto power_on_level = channel->dali->base.getPowerOnLevel(short_address);
|
||||
if (!power_on_level.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
settings.power_on_level = static_cast<uint8_t>(*power_on_level);
|
||||
if (const auto value = channel->dali->base.getSystemFailureLevel(short_address);
|
||||
value.has_value()) {
|
||||
settings.system_failure_level = static_cast<uint8_t>(*value);
|
||||
@@ -1021,8 +1346,11 @@ std::optional<DaliAddressSettingsSnapshot> DaliDomainService::queryAddressSettin
|
||||
bool DaliDomainService::applyGroupMask(uint8_t gateway_id, int short_address,
|
||||
uint16_t group_mask) const {
|
||||
const auto* channel = findChannelByGateway(gateway_id);
|
||||
return channel != nullptr && channel->dali != nullptr &&
|
||||
channel->dali->base.setGroup(short_address, group_mask);
|
||||
if (channel == nullptr || channel->dali == nullptr) {
|
||||
return false;
|
||||
}
|
||||
markBusActivity(gateway_id);
|
||||
return channel->dali->base.setGroup(short_address, group_mask);
|
||||
}
|
||||
|
||||
bool DaliDomainService::applySceneLevel(uint8_t gateway_id, int short_address, int scene,
|
||||
@@ -1032,6 +1360,8 @@ bool DaliDomainService::applySceneLevel(uint8_t gateway_id, int short_address, i
|
||||
return false;
|
||||
}
|
||||
|
||||
markBusActivity(gateway_id);
|
||||
|
||||
if (*level == 255U) {
|
||||
return channel->dali->base.removeScene(short_address, scene);
|
||||
}
|
||||
@@ -1047,6 +1377,8 @@ bool DaliDomainService::applyAddressSettings(uint8_t gateway_id, int short_addre
|
||||
return false;
|
||||
}
|
||||
|
||||
markBusActivity(gateway_id);
|
||||
|
||||
bool ok = true;
|
||||
if (settings.power_on_level.has_value()) {
|
||||
ok = ok && channel->dali->base.setPowerOnLevel(short_address, *settings.power_on_level);
|
||||
@@ -1232,7 +1564,7 @@ void DaliDomainService::rawFrameTaskLoop() {
|
||||
Dali_msg_t message = {};
|
||||
while (true) {
|
||||
if (queue == nullptr) {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
queue = dali_hal_raw_receive_queue();
|
||||
continue;
|
||||
}
|
||||
@@ -1250,6 +1582,12 @@ void DaliDomainService::rawFrameTaskLoop() {
|
||||
if (byte_count > DALI_MAX_BYTES) {
|
||||
byte_count = DALI_MAX_BYTES;
|
||||
}
|
||||
if (byte_count == 1 && TakeHardwareQueryRawSuppress(message.id)) {
|
||||
continue;
|
||||
}
|
||||
if (byte_count != 1 && byte_count != 2 && byte_count != 3) {
|
||||
continue;
|
||||
}
|
||||
DaliRawFrame frame;
|
||||
frame.channel_index = channel->config.channel_index;
|
||||
frame.gateway_id = channel->config.gateway_id;
|
||||
@@ -1260,6 +1598,7 @@ void DaliDomainService::rawFrameTaskLoop() {
|
||||
}
|
||||
|
||||
void DaliDomainService::notifyRawFrameSinks(const DaliRawFrame& frame) {
|
||||
markBusActivity(frame.gateway_id);
|
||||
if (raw_frame_sink_lock_ != nullptr) {
|
||||
xSemaphoreTake(raw_frame_sink_lock_, portMAX_DELAY);
|
||||
}
|
||||
@@ -1272,6 +1611,16 @@ void DaliDomainService::notifyRawFrameSinks(const DaliRawFrame& frame) {
|
||||
}
|
||||
}
|
||||
|
||||
void DaliDomainService::markBusActivity(uint8_t gateway_id) const {
|
||||
if (bus_activity_lock_ != nullptr) {
|
||||
xSemaphoreTake(bus_activity_lock_, portMAX_DELAY);
|
||||
}
|
||||
last_bus_activity_ticks_[gateway_id] = xTaskGetTickCount();
|
||||
if (bus_activity_lock_ != nullptr) {
|
||||
xSemaphoreGive(bus_activity_lock_);
|
||||
}
|
||||
}
|
||||
|
||||
bool DaliDomainService::hasSerialPort(int uart_port) const {
|
||||
return std::any_of(channels_.begin(), channels_.end(), [uart_port](const auto& channel) {
|
||||
return channel->serial_bus.has_value() && channel->serial_bus->uart_port == uart_port;
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include "gateway_controller.hpp"
|
||||
#include "gateway_runtime.hpp"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "host/ble_gap.h"
|
||||
@@ -29,6 +32,8 @@ constexpr uint16_t kChannel2Uuid = 0xFFF2;
|
||||
constexpr uint16_t kGatewayUuid = 0xFFF3;
|
||||
constexpr int64_t kGenericDedupeWindowUs = 120000;
|
||||
constexpr size_t kGatewayCharacteristicIndex = 2;
|
||||
constexpr int kGatewayNotifyAllocationAttempts = 6;
|
||||
constexpr TickType_t kGatewayNotifyRetryDelayTicks = pdMS_TO_TICKS(20);
|
||||
|
||||
gateway::GatewayBleBridge* s_active_bridge = nullptr;
|
||||
uint16_t s_value_handles[3] = {0, 0, 0};
|
||||
@@ -141,6 +146,13 @@ void RegisterGatt(struct ble_gatt_register_ctxt* ctxt, void* arg) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> LegacyRawPayload(const std::vector<uint8_t>& data) {
|
||||
if (data.size() == 1) {
|
||||
return {0xBE, data[0]};
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
const struct ble_gatt_svc_def kGattServices[] = {
|
||||
{
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
@@ -341,9 +353,25 @@ void GatewayBleBridge::notifyCharacteristic(size_t index, const std::vector<uint
|
||||
}
|
||||
|
||||
characteristic_values_[index] = payload;
|
||||
struct os_mbuf* buffer = ble_hs_mbuf_from_flat(payload.data(), payload.size());
|
||||
const int allocation_attempts =
|
||||
index == kGatewayCharacteristicIndex ? kGatewayNotifyAllocationAttempts : 1;
|
||||
struct os_mbuf* buffer = nullptr;
|
||||
for (int attempt = 0; attempt < allocation_attempts; ++attempt) {
|
||||
if (conn_handle_ == kInvalidConnectionHandle || !notify_enabled_[index]) {
|
||||
return;
|
||||
}
|
||||
buffer = ble_hs_mbuf_from_flat(payload.data(), payload.size());
|
||||
if (buffer != nullptr) {
|
||||
break;
|
||||
}
|
||||
if (attempt + 1 < allocation_attempts) {
|
||||
vTaskDelay(kGatewayNotifyRetryDelayTicks);
|
||||
}
|
||||
}
|
||||
if (buffer == nullptr) {
|
||||
ESP_LOGW(kTag, "failed to allocate notify mbuf idx=%u", static_cast<unsigned>(index));
|
||||
ESP_LOGW(kTag, "failed to allocate notify mbuf idx=%u attempts=%d len=%u",
|
||||
static_cast<unsigned>(index), allocation_attempts,
|
||||
static_cast<unsigned>(payload.size()));
|
||||
return;
|
||||
}
|
||||
const int rc = ble_gatts_notify_custom(conn_handle_, s_value_handles[index], buffer);
|
||||
@@ -377,7 +405,7 @@ void GatewayBleBridge::handleDaliRawFrame(const DaliRawFrame& frame) {
|
||||
if (!enabled_ || conn_handle_ == kInvalidConnectionHandle || frame.data.empty()) {
|
||||
return;
|
||||
}
|
||||
notifyCharacteristic(frame.channel_index, frame.data);
|
||||
notifyCharacteristic(frame.channel_index, LegacyRawPayload(frame.data));
|
||||
}
|
||||
|
||||
void GatewayBleBridge::handleRawWrite(size_t channel_index, const std::vector<uint8_t>& payload) {
|
||||
@@ -448,6 +476,18 @@ int GatewayBleBridge::handleGapEvent(struct ble_gap_event* event) {
|
||||
notify_enabled_.fill(false);
|
||||
last_notify_payload_.clear();
|
||||
last_notify_at_us_ = 0;
|
||||
struct ble_gap_upd_params params = {
|
||||
.itvl_min = 15,
|
||||
.itvl_max = 15,
|
||||
.latency = 3,
|
||||
.supervision_timeout = 1000,
|
||||
.min_ce_len = 0,
|
||||
.max_ce_len = 0,
|
||||
};
|
||||
int rc = ble_gap_update_params(event->connect.conn_handle, ¶ms);
|
||||
if (rc != 0) {
|
||||
ESP_LOGW(kTag, "ble_gap_update_params rc=%d", rc);
|
||||
}
|
||||
ESP_LOGI(kTag, "BLE client connected handle=%u", conn_handle_);
|
||||
} else if (enabled_) {
|
||||
startAdvertising();
|
||||
|
||||
@@ -7,14 +7,16 @@ set(GATEWAY_BRIDGE_REQUIRES
|
||||
gateway_cache
|
||||
gateway_knx
|
||||
gateway_modbus
|
||||
knx
|
||||
log
|
||||
lwip
|
||||
nvs_flash
|
||||
openknx_idf
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
SRCS "src/gateway_bridge.cpp"
|
||||
SRCS
|
||||
"src/gateway_bridge.cpp"
|
||||
"src/security_storage.cpp"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES ${GATEWAY_BRIDGE_REQUIRES}
|
||||
PRIV_REQUIRES gateway_bacnet
|
||||
|
||||
@@ -61,6 +61,7 @@ class GatewayBridgeService {
|
||||
const std::string& query = {});
|
||||
GatewayBridgeHttpResponse handlePost(const std::string& action, int gateway_id,
|
||||
const std::string& body);
|
||||
std::string handleTransportRequest(uint8_t gateway_id, std::string_view request);
|
||||
|
||||
private:
|
||||
struct ChannelRuntime;
|
||||
@@ -72,9 +73,10 @@ 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);
|
||||
DaliBridgeResult routeKnxGroupObjectWrite(uint16_t group_object_number,
|
||||
const uint8_t* data, size_t len);
|
||||
void handleDaliRawFrame(const DaliRawFrame& frame);
|
||||
void collectUsedRuntimeResources(uint8_t except_gateway_id,
|
||||
std::set<uint16_t>* modbus_tcp_ports,
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gateway_knx.hpp"
|
||||
|
||||
namespace gateway::openknx {
|
||||
|
||||
struct FactoryFdskInfo {
|
||||
bool available{false};
|
||||
std::string serialNumber;
|
||||
std::string label;
|
||||
std::string qrCode;
|
||||
};
|
||||
|
||||
struct FactoryCertificatePayload {
|
||||
bool available{false};
|
||||
std::string productIdentity;
|
||||
std::string manufacturerId;
|
||||
std::string applicationNumber;
|
||||
std::string applicationVersion;
|
||||
std::string serialNumber;
|
||||
std::string fdskLabel;
|
||||
std::string fdskQrCode;
|
||||
std::string storage;
|
||||
std::string createdAt;
|
||||
std::string checksum;
|
||||
};
|
||||
|
||||
struct IpSecureCredentialStatus {
|
||||
bool activated{false};
|
||||
bool backboneKeyAvailable{false};
|
||||
bool deviceAuthenticationKeyAvailable{false};
|
||||
uint8_t tunnelUserCount{0};
|
||||
uint64_t routingSequence{0};
|
||||
};
|
||||
|
||||
bool LoadFactoryFdsk(uint8_t* data, size_t len);
|
||||
FactoryFdskInfo LoadFactoryFdskInfo();
|
||||
bool LoadFactoryFdskForInstance(uint32_t instance_id, uint8_t* data, size_t len);
|
||||
FactoryFdskInfo LoadFactoryFdskInfoForInstance(uint32_t instance_id);
|
||||
bool GenerateFactoryFdsk(FactoryFdskInfo* info = nullptr);
|
||||
bool WriteFactoryFdskHex(const std::string& hex_key, FactoryFdskInfo* info = nullptr);
|
||||
bool ResetFactoryFdskCache(FactoryFdskInfo* info = nullptr);
|
||||
bool ResetFactorySecurityForInstance(uint32_t instance_id, FactoryFdskInfo* info = nullptr);
|
||||
FactoryCertificatePayload BuildFactoryCertificatePayload();
|
||||
FactoryCertificatePayload BuildFactoryCertificatePayloadForInstance(uint32_t instance_id);
|
||||
|
||||
bool LoadOamFactoryFdsk(uint8_t* data, size_t len);
|
||||
FactoryFdskInfo LoadOamFactoryFdskInfo();
|
||||
bool GenerateOamFactoryFdsk(FactoryFdskInfo* info = nullptr);
|
||||
bool WriteOamFactoryFdskHex(const std::string& hex_key,
|
||||
FactoryFdskInfo* info = nullptr);
|
||||
bool ResetOamFactoryFdskCache(FactoryFdskInfo* info = nullptr);
|
||||
FactoryCertificatePayload BuildOamFactoryCertificatePayload();
|
||||
|
||||
IpSecureCredentialStatus LoadOamIpSecureCredentialStatus();
|
||||
::gateway::GatewayKnxIpSecureCredentialMaterial LoadOamIpSecureCredentialMaterial();
|
||||
bool WriteOamIpSecureKeyringHex(const std::string& backbone_key_hex,
|
||||
const std::vector<std::string>& tunnel_user_key_hex,
|
||||
const std::string& device_auth_key_hex,
|
||||
bool activated);
|
||||
bool StoreOamIpSecureRoutingSequence(uint64_t sequence);
|
||||
bool ClearOamIpSecureKeyring();
|
||||
|
||||
} // namespace gateway::openknx
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,713 @@
|
||||
#include "security_storage.h"
|
||||
|
||||
#include "gateway_knx_internal.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_timer.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kTag = "openknx_sec";
|
||||
constexpr const char* kNamespace = "knx_sec";
|
||||
constexpr const char* kOamNamespace = "knx_oam_sec";
|
||||
constexpr const char* kFactoryFdskKey = "factory_fdsk";
|
||||
constexpr const char* kIpSecureBackboneKey = "ipsec_backbone";
|
||||
constexpr const char* kIpSecureDeviceAuthKey = "ipsec_dev_auth";
|
||||
constexpr const char* kIpSecureTunnelCountKey = "ipsec_tunnels";
|
||||
constexpr const char* kIpSecureActivatedKey = "ipsec_active";
|
||||
constexpr const char* kIpSecureRoutingSeqKey = "ipsec_route_seq";
|
||||
constexpr size_t kFdskSize = 16;
|
||||
constexpr size_t kSerialSize = 6;
|
||||
constexpr size_t kFdskQrSize = 36;
|
||||
constexpr const char* kProductIdentity = "REG1-Dali";
|
||||
constexpr const char* kOamProductIdentity = "OpenKNX-IP-Router";
|
||||
constexpr const char* kDevelopmentStorage = "base_mac_derived_plain_nvs_development";
|
||||
constexpr char kFdskDerivationLabel[] = "DaliMaster REG1-Dali deterministic FDSK v1";
|
||||
constexpr char kOamFdskDerivationLabel[] = "DaliMaster OAM-IP-Router deterministic FDSK v1";
|
||||
constexpr uint8_t kCrc4Tab[16] = {
|
||||
0x0, 0x3, 0x6, 0x5, 0xc, 0xf, 0xa, 0x9,
|
||||
0xb, 0x8, 0xd, 0xe, 0x7, 0x4, 0x1, 0x2,
|
||||
};
|
||||
constexpr char kBase32Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
constexpr char kHexAlphabet[] = "0123456789ABCDEF";
|
||||
|
||||
struct FactoryFdskContext {
|
||||
std::string nvsNamespace;
|
||||
const char* productIdentity;
|
||||
const char* derivationLabel;
|
||||
uint16_t manufacturerId;
|
||||
uint16_t applicationNumber;
|
||||
uint8_t applicationVersion;
|
||||
uint32_t serialMacIncrement;
|
||||
bool clearOpenKnxCache;
|
||||
};
|
||||
|
||||
const FactoryFdskContext kReg1Context{
|
||||
kNamespace,
|
||||
kProductIdentity,
|
||||
kFdskDerivationLabel,
|
||||
gateway::knx_internal::kReg1DaliManufacturerId,
|
||||
gateway::knx_internal::kReg1DaliApplicationNumber,
|
||||
gateway::knx_internal::kReg1DaliApplicationVersion,
|
||||
gateway::knx_internal::kReg1DaliSerialMacIncrement,
|
||||
true};
|
||||
|
||||
const FactoryFdskContext kOamContext{
|
||||
kOamNamespace,
|
||||
kOamProductIdentity,
|
||||
kOamFdskDerivationLabel,
|
||||
gateway::knx_internal::kOamRouterManufacturerId,
|
||||
gateway::knx_internal::kOamRouterApplicationNumber,
|
||||
gateway::knx_internal::kOamRouterApplicationVersion,
|
||||
gateway::knx_internal::kOamRouterSerialMacIncrement,
|
||||
false};
|
||||
|
||||
extern "C" void knx_platform_clear_cached_fdsk() __attribute__((weak));
|
||||
|
||||
FactoryFdskContext reg1ContextForInstance(uint32_t instance_id) {
|
||||
FactoryFdskContext context = kReg1Context;
|
||||
context.serialMacIncrement = gateway::knx_internal::kReg1DaliSerialMacIncrement + instance_id;
|
||||
context.clearOpenKnxCache = instance_id == 0;
|
||||
if (instance_id != 0) {
|
||||
context.nvsNamespace = std::string(kNamespace) + "_" + std::to_string(instance_id);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (nvs_flash_erase() != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
return nvs_flash_init() == ESP_OK;
|
||||
}
|
||||
return err == ESP_OK || err == ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
bool plausibleKey(const uint8_t* data) {
|
||||
const bool all_zero = std::all_of(data, data + kFdskSize, [](uint8_t value) {
|
||||
return value == 0x00;
|
||||
});
|
||||
const bool all_ff = std::all_of(data, data + kFdskSize, [](uint8_t value) {
|
||||
return value == 0xff;
|
||||
});
|
||||
return !all_zero && !all_ff;
|
||||
}
|
||||
|
||||
bool readBaseMac(uint8_t* data) {
|
||||
if (data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (esp_efuse_mac_get_default(data) == ESP_OK) {
|
||||
return true;
|
||||
}
|
||||
return esp_read_mac(data, ESP_MAC_WIFI_STA) == ESP_OK;
|
||||
}
|
||||
|
||||
void clearOpenKnxFdskCache() {
|
||||
if (knx_platform_clear_cached_fdsk != nullptr) {
|
||||
knx_platform_clear_cached_fdsk();
|
||||
}
|
||||
}
|
||||
|
||||
int fromHexDigit(char value) {
|
||||
if (value >= '0' && value <= '9') {
|
||||
return value - '0';
|
||||
}
|
||||
if (value >= 'a' && value <= 'f') {
|
||||
return value - 'a' + 10;
|
||||
}
|
||||
if (value >= 'A' && value <= 'F') {
|
||||
return value - 'A' + 10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool parseHexKey(const std::string& value, uint8_t* out) {
|
||||
std::string digits;
|
||||
digits.reserve(value.size());
|
||||
for (char ch : value) {
|
||||
if (ch == ':' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') {
|
||||
continue;
|
||||
}
|
||||
if (fromHexDigit(ch) < 0) {
|
||||
return false;
|
||||
}
|
||||
digits.push_back(ch);
|
||||
}
|
||||
if (digits.size() != kFdskSize * 2U) {
|
||||
return false;
|
||||
}
|
||||
for (size_t index = 0; index < kFdskSize; ++index) {
|
||||
const int hi = fromHexDigit(digits[index * 2U]);
|
||||
const int lo = fromHexDigit(digits[index * 2U + 1U]);
|
||||
if (hi < 0 || lo < 0) {
|
||||
return false;
|
||||
}
|
||||
out[index] = static_cast<uint8_t>((hi << 4) | lo);
|
||||
}
|
||||
return plausibleKey(out);
|
||||
}
|
||||
|
||||
bool loadKnxSerialNumber(const FactoryFdskContext& context, uint8_t* serial) {
|
||||
if (serial == nullptr) {
|
||||
return false;
|
||||
}
|
||||
std::array<uint8_t, kSerialSize> mac{};
|
||||
if (!readBaseMac(mac.data())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t suffix = (static_cast<uint32_t>(mac[2]) << 24) |
|
||||
(static_cast<uint32_t>(mac[3]) << 16) |
|
||||
(static_cast<uint32_t>(mac[4]) << 8) |
|
||||
static_cast<uint32_t>(mac[5]);
|
||||
suffix += context.serialMacIncrement;
|
||||
|
||||
serial[0] = static_cast<uint8_t>((context.manufacturerId >> 8) & 0xff);
|
||||
serial[1] = static_cast<uint8_t>(context.manufacturerId & 0xff);
|
||||
serial[2] = static_cast<uint8_t>((suffix >> 24) & 0xff);
|
||||
serial[3] = static_cast<uint8_t>((suffix >> 16) & 0xff);
|
||||
serial[4] = static_cast<uint8_t>((suffix >> 8) & 0xff);
|
||||
serial[5] = static_cast<uint8_t>(suffix & 0xff);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deriveFactoryFdskFromSerial(const FactoryFdskContext& context,
|
||||
const uint8_t* serial, uint8_t* key) {
|
||||
if (serial == nullptr || key == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const size_t label_len = std::strlen(context.derivationLabel);
|
||||
std::vector<uint8_t> material(label_len + kSerialSize);
|
||||
std::copy(context.derivationLabel, context.derivationLabel + label_len, material.begin());
|
||||
std::copy(serial, serial + kSerialSize, material.begin() + label_len);
|
||||
|
||||
std::array<uint8_t, 32> digest{};
|
||||
if (mbedtls_sha256(material.data(), material.size(), digest.data(), 0) != 0) {
|
||||
return false;
|
||||
}
|
||||
std::copy(digest.begin(), digest.begin() + kFdskSize, key);
|
||||
if (!plausibleKey(key)) {
|
||||
key[kFdskSize - 1] ^= 0xA5;
|
||||
}
|
||||
return plausibleKey(key);
|
||||
}
|
||||
|
||||
void syncFactoryFdskToNvs(const FactoryFdskContext& context, const uint8_t* data) {
|
||||
if (data == nullptr || !plausibleKey(data) || !ensureNvsReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<uint8_t, kFdskSize> stored{};
|
||||
size_t stored_size = stored.size();
|
||||
|
||||
nvs_handle_t handle = 0;
|
||||
esp_err_t err = nvs_open(context.nvsNamespace.c_str(), NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(kTag, "failed to open KNX security NVS namespace: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
err = nvs_get_blob(handle, kFactoryFdskKey, stored.data(), &stored_size);
|
||||
if (err == ESP_OK && stored_size == stored.size() &&
|
||||
std::equal(stored.begin(), stored.end(), data)) {
|
||||
nvs_close(handle);
|
||||
return;
|
||||
}
|
||||
err = nvs_set_blob(handle, kFactoryFdskKey, data, kFdskSize);
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(kTag, "failed to mirror deterministic KNX factory FDSK: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
if (context.clearOpenKnxCache) {
|
||||
clearOpenKnxFdskCache();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t crc4Array(const uint8_t* data, size_t len) {
|
||||
uint8_t crc = 0;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
crc = kCrc4Tab[crc ^ (data[i] >> 4)];
|
||||
crc = kCrc4Tab[crc ^ (data[i] & 0x0f)];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
std::string toBase32NoPadding(const uint8_t* data, size_t len) {
|
||||
std::string result;
|
||||
result.reserve(((len * 8) + 4) / 5);
|
||||
|
||||
uint32_t buffer = 0;
|
||||
int bits_left = 0;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
buffer = (buffer << 8) | data[i];
|
||||
bits_left += 8;
|
||||
while (bits_left >= 5) {
|
||||
const uint8_t index = static_cast<uint8_t>((buffer >> (bits_left - 5)) & 0x1f);
|
||||
result.push_back(kBase32Alphabet[index]);
|
||||
bits_left -= 5;
|
||||
}
|
||||
}
|
||||
|
||||
if (bits_left > 0) {
|
||||
const uint8_t index = static_cast<uint8_t>((buffer << (5 - bits_left)) & 0x1f);
|
||||
result.push_back(kBase32Alphabet[index]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string toHex(const uint8_t* data, size_t len) {
|
||||
std::string result;
|
||||
result.reserve(len * 2);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
result.push_back(kHexAlphabet[(data[i] >> 4) & 0x0f]);
|
||||
result.push_back(kHexAlphabet[data[i] & 0x0f]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string generateFdskQrCode(const uint8_t* serial, const uint8_t* key) {
|
||||
std::array<uint8_t, kSerialSize + kFdskSize + 1> buffer{};
|
||||
std::copy(serial, serial + kSerialSize, buffer.begin());
|
||||
std::copy(key, key + kFdskSize, buffer.begin() + kSerialSize);
|
||||
buffer[kSerialSize + kFdskSize] = static_cast<uint8_t>((crc4Array(buffer.data(), buffer.size() - 1) << 4) & 0xff);
|
||||
|
||||
std::string encoded = toBase32NoPadding(buffer.data(), buffer.size());
|
||||
if (encoded.size() > kFdskQrSize) {
|
||||
encoded.resize(kFdskQrSize);
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
std::string formatFdskLabel(const std::string& qr_code) {
|
||||
std::string label;
|
||||
label.reserve(qr_code.size() + (qr_code.size() / 6));
|
||||
for (size_t i = 0; i < qr_code.size(); ++i) {
|
||||
if (i != 0 && (i % 6) == 0) {
|
||||
label.push_back('-');
|
||||
}
|
||||
label.push_back(qr_code[i]);
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
std::string fnv1aHex(const std::string& value) {
|
||||
uint32_t hash = 2166136261u;
|
||||
for (unsigned char ch : value) {
|
||||
hash ^= ch;
|
||||
hash *= 16777619u;
|
||||
}
|
||||
std::array<uint8_t, 4> bytes{
|
||||
static_cast<uint8_t>((hash >> 24) & 0xff),
|
||||
static_cast<uint8_t>((hash >> 16) & 0xff),
|
||||
static_cast<uint8_t>((hash >> 8) & 0xff),
|
||||
static_cast<uint8_t>(hash & 0xff),
|
||||
};
|
||||
return toHex(bytes.data(), bytes.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace gateway::openknx {
|
||||
|
||||
bool LoadFactoryFdskForContext(const FactoryFdskContext& context, uint8_t* data, size_t len) {
|
||||
if (data == nullptr || len < kFdskSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<uint8_t, kSerialSize> serial{};
|
||||
std::array<uint8_t, kFdskSize> key{};
|
||||
if (!loadKnxSerialNumber(context, serial.data()) ||
|
||||
!deriveFactoryFdskFromSerial(context, serial.data(), key.data())) {
|
||||
return false;
|
||||
}
|
||||
std::memcpy(data, key.data(), kFdskSize);
|
||||
syncFactoryFdskToNvs(context, key.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
FactoryFdskInfo LoadFactoryFdskInfoForContext(const FactoryFdskContext& context) {
|
||||
FactoryFdskInfo info;
|
||||
std::array<uint8_t, kFdskSize> key{};
|
||||
std::array<uint8_t, kSerialSize> serial{};
|
||||
if (!loadKnxSerialNumber(context, serial.data()) ||
|
||||
!LoadFactoryFdskForContext(context, key.data(), key.size())) {
|
||||
return info;
|
||||
}
|
||||
|
||||
info.available = true;
|
||||
info.serialNumber = toHex(serial.data(), serial.size());
|
||||
info.qrCode = generateFdskQrCode(serial.data(), key.data());
|
||||
info.label = formatFdskLabel(info.qrCode);
|
||||
return info;
|
||||
}
|
||||
|
||||
bool GenerateFactoryFdskForContext(const FactoryFdskContext& context,
|
||||
FactoryFdskInfo* info) {
|
||||
std::array<uint8_t, kFdskSize> key{};
|
||||
const bool stored = LoadFactoryFdskForContext(context, key.data(), key.size());
|
||||
std::fill(key.begin(), key.end(), 0);
|
||||
if (!stored) {
|
||||
return false;
|
||||
}
|
||||
if (info != nullptr) {
|
||||
*info = LoadFactoryFdskInfoForContext(context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteFactoryFdskHexForContext(const FactoryFdskContext& context,
|
||||
const std::string& hex_key,
|
||||
FactoryFdskInfo* info) {
|
||||
std::array<uint8_t, kFdskSize> key{};
|
||||
if (!parseHexKey(hex_key, key.data())) {
|
||||
return false;
|
||||
}
|
||||
std::array<uint8_t, kSerialSize> serial{};
|
||||
std::array<uint8_t, kFdskSize> derived{};
|
||||
const bool stored = loadKnxSerialNumber(context, serial.data()) &&
|
||||
deriveFactoryFdskFromSerial(context, serial.data(), derived.data()) &&
|
||||
std::equal(key.begin(), key.end(), derived.begin());
|
||||
if (stored) {
|
||||
syncFactoryFdskToNvs(context, derived.data());
|
||||
}
|
||||
std::fill(key.begin(), key.end(), 0);
|
||||
std::fill(derived.begin(), derived.end(), 0);
|
||||
if (!stored) {
|
||||
return false;
|
||||
}
|
||||
if (info != nullptr) {
|
||||
*info = LoadFactoryFdskInfoForContext(context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResetFactoryFdskCacheForContext(const FactoryFdskContext& context,
|
||||
FactoryFdskInfo* info) {
|
||||
if (context.clearOpenKnxCache) {
|
||||
clearOpenKnxFdskCache();
|
||||
}
|
||||
const auto loaded = LoadFactoryFdskInfoForContext(context);
|
||||
if (info != nullptr) {
|
||||
*info = loaded;
|
||||
}
|
||||
return loaded.available;
|
||||
}
|
||||
|
||||
FactoryCertificatePayload BuildFactoryCertificatePayloadForContext(
|
||||
const FactoryFdskContext& context) {
|
||||
FactoryCertificatePayload payload;
|
||||
const auto info = LoadFactoryFdskInfoForContext(context);
|
||||
if (!info.available) {
|
||||
return payload;
|
||||
}
|
||||
payload.available = true;
|
||||
payload.productIdentity = context.productIdentity;
|
||||
payload.manufacturerId = hexValue(context.manufacturerId, 4);
|
||||
payload.applicationNumber = hexValue(context.applicationNumber,
|
||||
context.applicationNumber <= 0xff ? 2 : 4);
|
||||
payload.applicationVersion = hexValue(context.applicationVersion, 2);
|
||||
payload.serialNumber = info.serialNumber;
|
||||
payload.fdskLabel = info.label;
|
||||
payload.fdskQrCode = info.qrCode;
|
||||
payload.storage = kDevelopmentStorage;
|
||||
payload.createdAt = "uptime_us:" + std::to_string(esp_timer_get_time());
|
||||
payload.checksum = fnv1aHex(payload.productIdentity + "|" + payload.manufacturerId + "|" +
|
||||
payload.applicationNumber + "|" + payload.applicationVersion + "|" +
|
||||
payload.serialNumber + "|" + payload.fdskLabel + "|" +
|
||||
payload.fdskQrCode + "|" + payload.createdAt);
|
||||
return payload;
|
||||
}
|
||||
|
||||
std::string TunnelUserKeyName(uint8_t index) {
|
||||
std::array<char, 18> buffer{};
|
||||
std::snprintf(buffer.data(), buffer.size(), "ipsec_tunnel_%02u",
|
||||
static_cast<unsigned>(index));
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
bool BlobKeyAvailable(nvs_handle_t handle, const char* key) {
|
||||
std::array<uint8_t, kFdskSize> data{};
|
||||
size_t len = data.size();
|
||||
return nvs_get_blob(handle, key, data.data(), &len) == ESP_OK && len == data.size() &&
|
||||
plausibleKey(data.data());
|
||||
}
|
||||
|
||||
bool SetOptionalHexBlob(nvs_handle_t handle, const char* key, const std::string& hex) {
|
||||
if (hex.empty()) {
|
||||
nvs_erase_key(handle, key);
|
||||
return true;
|
||||
}
|
||||
std::array<uint8_t, kFdskSize> data{};
|
||||
if (!parseHexKey(hex, data.data())) {
|
||||
return false;
|
||||
}
|
||||
const esp_err_t err = nvs_set_blob(handle, key, data.data(), data.size());
|
||||
std::fill(data.begin(), data.end(), 0);
|
||||
return err == ESP_OK;
|
||||
}
|
||||
|
||||
bool LoadOptionalKey(nvs_handle_t handle, const char* key, std::array<uint8_t, kFdskSize>* out) {
|
||||
if (out == nullptr) {
|
||||
return false;
|
||||
}
|
||||
std::array<uint8_t, kFdskSize> data{};
|
||||
size_t len = data.size();
|
||||
if (nvs_get_blob(handle, key, data.data(), &len) != ESP_OK || len != data.size() ||
|
||||
!plausibleKey(data.data())) {
|
||||
return false;
|
||||
}
|
||||
*out = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadFactoryFdsk(uint8_t* data, size_t len) {
|
||||
return LoadFactoryFdskForContext(kReg1Context, data, len);
|
||||
}
|
||||
|
||||
FactoryFdskInfo LoadFactoryFdskInfo() {
|
||||
return LoadFactoryFdskInfoForContext(kReg1Context);
|
||||
}
|
||||
|
||||
bool LoadFactoryFdskForInstance(uint32_t instance_id, uint8_t* data, size_t len) {
|
||||
const auto context = reg1ContextForInstance(instance_id);
|
||||
return LoadFactoryFdskForContext(context, data, len);
|
||||
}
|
||||
|
||||
FactoryFdskInfo LoadFactoryFdskInfoForInstance(uint32_t instance_id) {
|
||||
const auto context = reg1ContextForInstance(instance_id);
|
||||
return LoadFactoryFdskInfoForContext(context);
|
||||
}
|
||||
|
||||
bool GenerateFactoryFdsk(FactoryFdskInfo* info) {
|
||||
return GenerateFactoryFdskForContext(kReg1Context, info);
|
||||
}
|
||||
|
||||
bool WriteFactoryFdskHex(const std::string& hex_key, FactoryFdskInfo* info) {
|
||||
return WriteFactoryFdskHexForContext(kReg1Context, hex_key, info);
|
||||
}
|
||||
|
||||
bool ResetFactoryFdskCache(FactoryFdskInfo* info) {
|
||||
return ResetFactoryFdskCacheForContext(kReg1Context, info);
|
||||
}
|
||||
|
||||
bool ResetFactorySecurityForInstance(uint32_t instance_id, FactoryFdskInfo* info) {
|
||||
const auto context = reg1ContextForInstance(instance_id);
|
||||
if (ensureNvsReady()) {
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(context.nvsNamespace.c_str(), NVS_READWRITE, &handle) == ESP_OK) {
|
||||
nvs_erase_key(handle, kFactoryFdskKey);
|
||||
nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
}
|
||||
}
|
||||
return ResetFactoryFdskCacheForContext(context, info);
|
||||
}
|
||||
|
||||
FactoryCertificatePayload BuildFactoryCertificatePayload() {
|
||||
return BuildFactoryCertificatePayloadForContext(kReg1Context);
|
||||
}
|
||||
|
||||
FactoryCertificatePayload BuildFactoryCertificatePayloadForInstance(uint32_t instance_id) {
|
||||
const auto context = reg1ContextForInstance(instance_id);
|
||||
return BuildFactoryCertificatePayloadForContext(context);
|
||||
}
|
||||
|
||||
bool LoadOamFactoryFdsk(uint8_t* data, size_t len) {
|
||||
return LoadFactoryFdskForContext(kOamContext, data, len);
|
||||
}
|
||||
|
||||
FactoryFdskInfo LoadOamFactoryFdskInfo() {
|
||||
return LoadFactoryFdskInfoForContext(kOamContext);
|
||||
}
|
||||
|
||||
bool GenerateOamFactoryFdsk(FactoryFdskInfo* info) {
|
||||
return GenerateFactoryFdskForContext(kOamContext, info);
|
||||
}
|
||||
|
||||
bool WriteOamFactoryFdskHex(const std::string& hex_key, FactoryFdskInfo* info) {
|
||||
return WriteFactoryFdskHexForContext(kOamContext, hex_key, info);
|
||||
}
|
||||
|
||||
bool ResetOamFactoryFdskCache(FactoryFdskInfo* info) {
|
||||
return ResetFactoryFdskCacheForContext(kOamContext, info);
|
||||
}
|
||||
|
||||
FactoryCertificatePayload BuildOamFactoryCertificatePayload() {
|
||||
return BuildFactoryCertificatePayloadForContext(kOamContext);
|
||||
}
|
||||
|
||||
IpSecureCredentialStatus LoadOamIpSecureCredentialStatus() {
|
||||
IpSecureCredentialStatus status;
|
||||
if (!ensureNvsReady()) {
|
||||
return status;
|
||||
}
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(kOamNamespace, NVS_READONLY, &handle) != ESP_OK) {
|
||||
return status;
|
||||
}
|
||||
uint8_t activated = 0;
|
||||
if (nvs_get_u8(handle, kIpSecureActivatedKey, &activated) == ESP_OK) {
|
||||
status.activated = activated != 0;
|
||||
}
|
||||
uint8_t tunnel_count = 0;
|
||||
if (nvs_get_u8(handle, kIpSecureTunnelCountKey, &tunnel_count) == ESP_OK) {
|
||||
status.tunnelUserCount = tunnel_count;
|
||||
}
|
||||
uint64_t routing_sequence = 0;
|
||||
if (nvs_get_u64(handle, kIpSecureRoutingSeqKey, &routing_sequence) == ESP_OK) {
|
||||
status.routingSequence = routing_sequence;
|
||||
}
|
||||
status.backboneKeyAvailable = BlobKeyAvailable(handle, kIpSecureBackboneKey);
|
||||
status.deviceAuthenticationKeyAvailable = BlobKeyAvailable(handle, kIpSecureDeviceAuthKey);
|
||||
nvs_close(handle);
|
||||
return status;
|
||||
}
|
||||
|
||||
::gateway::GatewayKnxIpSecureCredentialMaterial LoadOamIpSecureCredentialMaterial() {
|
||||
::gateway::GatewayKnxIpSecureCredentialMaterial material;
|
||||
if (!ensureNvsReady()) {
|
||||
return material;
|
||||
}
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(kOamNamespace, NVS_READONLY, &handle) != ESP_OK) {
|
||||
return material;
|
||||
}
|
||||
uint8_t activated = 0;
|
||||
if (nvs_get_u8(handle, kIpSecureActivatedKey, &activated) == ESP_OK) {
|
||||
material.activated = activated != 0;
|
||||
}
|
||||
material.backbone_key_available = LoadOptionalKey(handle, kIpSecureBackboneKey,
|
||||
&material.backbone_key);
|
||||
material.device_authentication_key_available =
|
||||
LoadOptionalKey(handle, kIpSecureDeviceAuthKey,
|
||||
&material.device_authentication_key);
|
||||
uint8_t tunnel_count = 0;
|
||||
if (nvs_get_u8(handle, kIpSecureTunnelCountKey, &tunnel_count) == ESP_OK) {
|
||||
tunnel_count = std::min<uint8_t>(tunnel_count, 16);
|
||||
material.tunnel_user_keys.reserve(tunnel_count);
|
||||
for (uint8_t index = 0; index < tunnel_count; ++index) {
|
||||
std::array<uint8_t, kFdskSize> key{};
|
||||
const std::string key_name = TunnelUserKeyName(index);
|
||||
if (LoadOptionalKey(handle, key_name.c_str(), &key)) {
|
||||
material.tunnel_user_keys.push_back(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
uint64_t routing_sequence = 0;
|
||||
if (nvs_get_u64(handle, kIpSecureRoutingSeqKey, &routing_sequence) == ESP_OK) {
|
||||
material.routing_sequence = routing_sequence;
|
||||
}
|
||||
nvs_close(handle);
|
||||
return material;
|
||||
}
|
||||
|
||||
bool WriteOamIpSecureKeyringHex(const std::string& backbone_key_hex,
|
||||
const std::vector<std::string>& tunnel_user_key_hex,
|
||||
const std::string& device_auth_key_hex,
|
||||
bool activated) {
|
||||
if (tunnel_user_key_hex.size() > 16 || !ensureNvsReady()) {
|
||||
return false;
|
||||
}
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(kOamNamespace, NVS_READWRITE, &handle) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
bool ok = SetOptionalHexBlob(handle, kIpSecureBackboneKey, backbone_key_hex) &&
|
||||
SetOptionalHexBlob(handle, kIpSecureDeviceAuthKey, device_auth_key_hex);
|
||||
for (uint8_t index = 0; index < 16; ++index) {
|
||||
const std::string key = TunnelUserKeyName(index);
|
||||
nvs_erase_key(handle, key.c_str());
|
||||
}
|
||||
if (ok) {
|
||||
for (size_t index = 0; index < tunnel_user_key_hex.size(); ++index) {
|
||||
const std::string key = TunnelUserKeyName(static_cast<uint8_t>(index));
|
||||
ok = SetOptionalHexBlob(handle, key.c_str(), tunnel_user_key_hex[index]);
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
ok = nvs_set_u8(handle, kIpSecureTunnelCountKey,
|
||||
static_cast<uint8_t>(tunnel_user_key_hex.size())) == ESP_OK &&
|
||||
nvs_set_u8(handle, kIpSecureActivatedKey, activated ? 1 : 0) == ESP_OK;
|
||||
}
|
||||
if (ok) {
|
||||
uint64_t existing_sequence = 0;
|
||||
if (nvs_get_u64(handle, kIpSecureRoutingSeqKey, &existing_sequence) != ESP_OK) {
|
||||
ok = nvs_set_u64(handle, kIpSecureRoutingSeqKey, 0) == ESP_OK;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
ok = nvs_commit(handle) == ESP_OK;
|
||||
}
|
||||
nvs_close(handle);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool StoreOamIpSecureRoutingSequence(uint64_t sequence) {
|
||||
if (!ensureNvsReady()) {
|
||||
return false;
|
||||
}
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(kOamNamespace, NVS_READWRITE, &handle) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
const bool ok = nvs_set_u64(handle, kIpSecureRoutingSeqKey, sequence) == ESP_OK &&
|
||||
nvs_commit(handle) == ESP_OK;
|
||||
nvs_close(handle);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ClearOamIpSecureKeyring() {
|
||||
if (!ensureNvsReady()) {
|
||||
return false;
|
||||
}
|
||||
nvs_handle_t handle = 0;
|
||||
if (nvs_open(kOamNamespace, NVS_READWRITE, &handle) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
nvs_erase_key(handle, kIpSecureBackboneKey);
|
||||
nvs_erase_key(handle, kIpSecureDeviceAuthKey);
|
||||
nvs_erase_key(handle, kIpSecureTunnelCountKey);
|
||||
nvs_erase_key(handle, kIpSecureActivatedKey);
|
||||
nvs_erase_key(handle, kIpSecureRoutingSeqKey);
|
||||
for (uint8_t index = 0; index < 16; ++index) {
|
||||
const std::string key = TunnelUserKeyName(index);
|
||||
nvs_erase_key(handle, key.c_str());
|
||||
}
|
||||
const bool ok = nvs_commit(handle) == ESP_OK;
|
||||
nvs_close(handle);
|
||||
return ok;
|
||||
}
|
||||
|
||||
} // namespace gateway::openknx
|
||||
|
||||
extern "C" bool knx_platform_get_fdsk(uint8_t* data, size_t len) {
|
||||
return gateway::openknx::LoadFactoryFdsk(data, len);
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -24,7 +25,8 @@ struct GatewayCacheConfig {
|
||||
bool cache_enabled{true};
|
||||
bool reconciliation_enabled{true};
|
||||
bool full_state_mirror_enabled{false};
|
||||
uint32_t flush_interval_ms{5000};
|
||||
uint32_t flush_interval_ms{10000};
|
||||
uint32_t refresh_interval_ms{120000};
|
||||
uint32_t task_stack_size{4096};
|
||||
UBaseType_t task_priority{3};
|
||||
GatewayCachePriorityMode default_priority_mode{GatewayCachePriorityMode::kOutsideBusFirst};
|
||||
@@ -41,6 +43,12 @@ enum class GatewayCacheDaliTargetKind : uint8_t {
|
||||
kBroadcast = 2,
|
||||
};
|
||||
|
||||
enum class GatewayCacheDaliPresence : uint8_t {
|
||||
kUnknown = 0,
|
||||
kOnline = 1,
|
||||
kOffline = 2,
|
||||
};
|
||||
|
||||
struct GatewayCacheDaliTarget {
|
||||
GatewayCacheDaliTargetKind kind{GatewayCacheDaliTargetKind::kShortAddress};
|
||||
uint8_t value{0};
|
||||
@@ -71,6 +79,7 @@ struct GatewayCacheDaliRuntimeStatus {
|
||||
std::optional<uint8_t> actual_level;
|
||||
std::optional<uint8_t> scene_id;
|
||||
bool use_min_level{false};
|
||||
bool stale{false};
|
||||
uint32_t revision{0};
|
||||
|
||||
bool anyKnown() const {
|
||||
@@ -136,6 +145,12 @@ class GatewayCache {
|
||||
GatewayCacheChannelFlags channelFlags(uint8_t gateway_id);
|
||||
GatewayCacheChannelFlags pendingChannelFlags(uint8_t gateway_id);
|
||||
GatewayCacheDaliAddressState daliAddressState(uint8_t gateway_id, uint8_t short_address);
|
||||
GatewayCacheDaliPresence daliAddressPresence(uint8_t gateway_id, uint8_t short_address);
|
||||
void markDaliAddressPresence(uint8_t gateway_id, uint8_t short_address,
|
||||
GatewayCacheDaliPresence presence);
|
||||
std::optional<GatewayCacheDaliTarget> decodeDaliTarget(uint8_t raw_addr);
|
||||
std::vector<uint8_t> reconciliationAddresses(
|
||||
uint8_t gateway_id, std::optional<GatewayCacheDaliTarget> target);
|
||||
GatewayCacheDaliRuntimeStatus daliGroupStatus(uint8_t gateway_id, uint8_t group_id);
|
||||
GatewayCacheDaliRuntimeStatus daliBroadcastStatus(uint8_t gateway_id);
|
||||
bool setDaliGroupMask(uint8_t gateway_id, uint8_t short_address,
|
||||
@@ -144,6 +159,8 @@ class GatewayCache {
|
||||
std::optional<uint8_t> level);
|
||||
bool setDaliSettings(uint8_t gateway_id, uint8_t short_address,
|
||||
std::optional<GatewayCacheDaliSettingsSnapshot> settings);
|
||||
bool setDaliActualLevel(uint8_t gateway_id, uint8_t short_address,
|
||||
std::optional<uint8_t> level);
|
||||
bool clearChannelFlagsIfMatched(uint8_t gateway_id, const GatewayCacheChannelFlags& flags);
|
||||
void markGroupUpdateNeeded(uint8_t gateway_id, bool needed = true);
|
||||
void markSceneUpdateNeeded(uint8_t gateway_id, bool needed = true);
|
||||
@@ -174,9 +191,13 @@ class GatewayCache {
|
||||
void closeStorageLocked();
|
||||
bool persistSceneLocked(uint8_t gateway_id, uint8_t scene_id, const SceneEntry& scene);
|
||||
bool persistGroupLocked(uint8_t gateway_id, uint8_t group_id, const GroupEntry& group);
|
||||
bool persistDaliAddressStateLocked(uint8_t gateway_id, uint8_t short_address,
|
||||
const GatewayCacheDaliAddressState& state);
|
||||
bool commitStorageLocked();
|
||||
bool shouldTrackUpdateFlagsLocked() const;
|
||||
uint32_t nextDaliRuntimeRevisionLocked();
|
||||
void markDaliAddressPresenceLocked(uint8_t gateway_id, uint8_t short_address,
|
||||
GatewayCacheDaliPresence presence);
|
||||
bool mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr, uint8_t command);
|
||||
void clearDaliTargetStateLocked(uint8_t gateway_id, const GatewayCacheDaliTarget& target,
|
||||
uint32_t revision);
|
||||
@@ -205,6 +226,8 @@ class GatewayCache {
|
||||
GroupStore& ensureGroupStoreLocked(uint8_t gateway_id);
|
||||
void loadSceneStoreLocked(uint8_t gateway_id, SceneStore& scenes);
|
||||
void loadGroupStoreLocked(uint8_t gateway_id, GroupStore& groups);
|
||||
void loadDaliStateStoreLocked(uint8_t gateway_id,
|
||||
std::array<GatewayCacheDaliAddressState, 64>& states);
|
||||
std::string readStringLocked(std::string_view key);
|
||||
bool writeStringLocked(std::string_view key, std::string_view value);
|
||||
bool eraseKeyLocked(std::string_view key);
|
||||
@@ -217,6 +240,7 @@ class GatewayCache {
|
||||
std::map<uint8_t, SceneStore> scenes_;
|
||||
std::map<uint8_t, GroupStore> groups_;
|
||||
std::map<uint8_t, std::array<GatewayCacheDaliAddressState, 64>> dali_states_;
|
||||
std::map<uint8_t, std::array<GatewayCacheDaliPresence, 64>> dali_presence_;
|
||||
std::map<uint8_t, std::array<GatewayCacheDaliRuntimeStatus, 16>> dali_group_status_;
|
||||
std::map<uint8_t, GatewayCacheDaliRuntimeStatus> dali_broadcast_status_;
|
||||
std::map<uint8_t, DtrState> dtr_states_;
|
||||
|
||||
@@ -43,6 +43,18 @@ constexpr uint8_t kDaliCmdDt8StoreDtrAsColorX = 0xE0;
|
||||
constexpr uint8_t kDaliCmdDt8StoreDtrAsColorY = 0xE1;
|
||||
constexpr uint8_t kDaliCmdDt8StorePrimaryMin = 0xF0;
|
||||
constexpr uint8_t kDaliCmdDt8StartAutoCalibration = 0xF6;
|
||||
constexpr int kDaliStatePayloadVersion = 1;
|
||||
constexpr uint32_t kDaliStateGroupMaskKnown = 1U << 0;
|
||||
constexpr uint32_t kDaliStateActualKnown = 1U << 1;
|
||||
constexpr uint32_t kDaliStateSceneKnown = 1U << 2;
|
||||
constexpr uint32_t kDaliStateUseMinLevel = 1U << 3;
|
||||
constexpr uint32_t kDaliStateStatusStale = 1U << 4;
|
||||
constexpr uint32_t kDaliStatePowerOnKnown = 1U << 5;
|
||||
constexpr uint32_t kDaliStateSystemFailureKnown = 1U << 6;
|
||||
constexpr uint32_t kDaliStateMinKnown = 1U << 7;
|
||||
constexpr uint32_t kDaliStateMaxKnown = 1U << 8;
|
||||
constexpr uint32_t kDaliStateFadeTimeKnown = 1U << 9;
|
||||
constexpr uint32_t kDaliStateFadeRateKnown = 1U << 10;
|
||||
|
||||
class LockGuard {
|
||||
public:
|
||||
@@ -134,6 +146,18 @@ bool ShouldMirrorObservedMutation(GatewayCacheRawFrameOrigin origin,
|
||||
priority_mode == GatewayCachePriorityMode::kOutsideBusFirst;
|
||||
}
|
||||
|
||||
bool ShouldAlwaysMirrorObservedStatus(uint8_t raw_addr, uint8_t command) {
|
||||
if (!DecodeDaliTarget(raw_addr).has_value()) {
|
||||
return false;
|
||||
}
|
||||
if ((raw_addr & 0x01) == 0) {
|
||||
return command <= 254;
|
||||
}
|
||||
return command == kDaliCmdOff || command == kDaliCmdRecallMax ||
|
||||
command == kDaliCmdRecallMin ||
|
||||
(command >= kDaliCmdGoToSceneMin && command <= kDaliCmdGoToSceneMax);
|
||||
}
|
||||
|
||||
void ClearDaliState(GatewayCacheDaliAddressState& state) {
|
||||
state.group_mask_known = false;
|
||||
state.group_mask = 0;
|
||||
@@ -215,6 +239,139 @@ std::string BuildGroupPayload(const GatewayCache::GroupEntry& group) {
|
||||
return std::string(payload);
|
||||
}
|
||||
|
||||
uint8_t ByteValue(int value) {
|
||||
return static_cast<uint8_t>(std::clamp(value, 0, 255));
|
||||
}
|
||||
|
||||
uint16_t WordValue(int value) {
|
||||
return static_cast<uint16_t>(std::clamp(value, 0, 0xffff));
|
||||
}
|
||||
|
||||
uint16_t SceneKnownMask(const GatewayCacheDaliAddressState& state) {
|
||||
uint16_t mask = 0;
|
||||
for (size_t index = 0; index < state.scene_levels.size(); ++index) {
|
||||
if (state.scene_levels[index].has_value()) {
|
||||
mask |= static_cast<uint16_t>(1U << index);
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
bool IsDefaultDaliAddressState(const GatewayCacheDaliAddressState& state) {
|
||||
return !state.group_mask_known && state.group_mask == 0 && SceneKnownMask(state) == 0 &&
|
||||
!state.settings.anyKnown() && !state.status.anyKnown();
|
||||
}
|
||||
|
||||
uint32_t DaliStateFlags(const GatewayCacheDaliAddressState& state) {
|
||||
uint32_t flags = 0;
|
||||
if (state.group_mask_known) {
|
||||
flags |= kDaliStateGroupMaskKnown;
|
||||
}
|
||||
if (state.status.actual_level.has_value()) {
|
||||
flags |= kDaliStateActualKnown;
|
||||
}
|
||||
if (state.status.scene_id.has_value()) {
|
||||
flags |= kDaliStateSceneKnown;
|
||||
}
|
||||
if (state.status.use_min_level) {
|
||||
flags |= kDaliStateUseMinLevel;
|
||||
}
|
||||
if (state.status.stale) {
|
||||
flags |= kDaliStateStatusStale;
|
||||
}
|
||||
if (state.settings.power_on_level.has_value()) {
|
||||
flags |= kDaliStatePowerOnKnown;
|
||||
}
|
||||
if (state.settings.system_failure_level.has_value()) {
|
||||
flags |= kDaliStateSystemFailureKnown;
|
||||
}
|
||||
if (state.settings.min_level.has_value()) {
|
||||
flags |= kDaliStateMinKnown;
|
||||
}
|
||||
if (state.settings.max_level.has_value()) {
|
||||
flags |= kDaliStateMaxKnown;
|
||||
}
|
||||
if (state.settings.fade_time.has_value()) {
|
||||
flags |= kDaliStateFadeTimeKnown;
|
||||
}
|
||||
if (state.settings.fade_rate.has_value()) {
|
||||
flags |= kDaliStateFadeRateKnown;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
std::string BuildDaliStatePayload(const GatewayCacheDaliAddressState& state) {
|
||||
const uint16_t scene_known_mask = SceneKnownMask(state);
|
||||
std::string payload = std::to_string(kDaliStatePayloadVersion);
|
||||
payload += "," + std::to_string(DaliStateFlags(state));
|
||||
payload += "," + std::to_string(state.status.revision);
|
||||
payload += "," + std::to_string(state.group_mask);
|
||||
payload += "," + std::to_string(state.status.actual_level.value_or(0));
|
||||
payload += "," + std::to_string(state.status.scene_id.value_or(0));
|
||||
payload += "," + std::to_string(state.settings.power_on_level.value_or(0));
|
||||
payload += "," + std::to_string(state.settings.system_failure_level.value_or(0));
|
||||
payload += "," + std::to_string(state.settings.min_level.value_or(0));
|
||||
payload += "," + std::to_string(state.settings.max_level.value_or(0));
|
||||
payload += "," + std::to_string(state.settings.fade_time.value_or(0));
|
||||
payload += "," + std::to_string(state.settings.fade_rate.value_or(0));
|
||||
payload += "," + std::to_string(scene_known_mask);
|
||||
for (const auto& scene_level : state.scene_levels) {
|
||||
payload += "," + std::to_string(scene_level.value_or(255));
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
void ApplyDaliStatePayload(std::string_view raw, GatewayCacheDaliAddressState& state) {
|
||||
const auto values = ParseCsv(raw);
|
||||
if (values.size() < 13 || values[0] != kDaliStatePayloadVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t flags = static_cast<uint32_t>(std::max(values[1], 0));
|
||||
state.group_mask_known = (flags & kDaliStateGroupMaskKnown) != 0;
|
||||
state.group_mask = state.group_mask_known ? WordValue(values[3]) : 0;
|
||||
|
||||
state.status = {};
|
||||
state.status.revision = static_cast<uint32_t>(std::max(values[2], 0));
|
||||
state.status.stale = (flags & kDaliStateStatusStale) != 0;
|
||||
state.status.use_min_level = (flags & kDaliStateUseMinLevel) != 0;
|
||||
if ((flags & kDaliStateActualKnown) != 0) {
|
||||
state.status.actual_level = ByteValue(values[4]);
|
||||
}
|
||||
if ((flags & kDaliStateSceneKnown) != 0) {
|
||||
state.status.scene_id = static_cast<uint8_t>(std::min<int>(ByteValue(values[5]), 15));
|
||||
}
|
||||
|
||||
state.settings = {};
|
||||
if ((flags & kDaliStatePowerOnKnown) != 0) {
|
||||
state.settings.power_on_level = ByteValue(values[6]);
|
||||
}
|
||||
if ((flags & kDaliStateSystemFailureKnown) != 0) {
|
||||
state.settings.system_failure_level = ByteValue(values[7]);
|
||||
}
|
||||
if ((flags & kDaliStateMinKnown) != 0) {
|
||||
state.settings.min_level = ByteValue(values[8]);
|
||||
}
|
||||
if ((flags & kDaliStateMaxKnown) != 0) {
|
||||
state.settings.max_level = ByteValue(values[9]);
|
||||
}
|
||||
if ((flags & kDaliStateFadeTimeKnown) != 0) {
|
||||
state.settings.fade_time = ByteValue(values[10]);
|
||||
}
|
||||
if ((flags & kDaliStateFadeRateKnown) != 0) {
|
||||
state.settings.fade_rate = ByteValue(values[11]);
|
||||
}
|
||||
|
||||
state.scene_levels.fill(std::nullopt);
|
||||
const uint16_t scene_known_mask = WordValue(values[12]);
|
||||
for (uint8_t scene_id = 0; scene_id < state.scene_levels.size(); ++scene_id) {
|
||||
const size_t value_index = 13 + scene_id;
|
||||
if ((scene_known_mask & (1U << scene_id)) != 0 && value_index < values.size()) {
|
||||
state.scene_levels[scene_id] = ByteValue(values[value_index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GatewayCache::GatewayCache(GatewayCacheConfig config)
|
||||
@@ -269,8 +426,11 @@ esp_err_t GatewayCache::start() {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
ESP_LOGI(kTag, "cache started namespace=%s flush_interval_ms=%u reconciliation=%d full_mirror=%d",
|
||||
ESP_LOGI(kTag,
|
||||
"cache started namespace=%s flush_interval_ms=%u refresh_interval_ms=%u "
|
||||
"reconciliation=%d full_mirror=%d",
|
||||
config_.storage_namespace.c_str(), static_cast<unsigned>(config_.flush_interval_ms),
|
||||
static_cast<unsigned>(config_.refresh_interval_ms),
|
||||
config_.reconciliation_enabled, config_.full_state_mirror_enabled);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -282,6 +442,10 @@ void GatewayCache::preloadChannel(uint8_t gateway_id) {
|
||||
}
|
||||
ensureSceneStoreLocked(gateway_id);
|
||||
ensureGroupStoreLocked(gateway_id);
|
||||
auto [it, inserted] = dali_states_.try_emplace(gateway_id);
|
||||
if (inserted) {
|
||||
loadDaliStateStoreLocked(gateway_id, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
GatewayCache::SceneStore GatewayCache::scenes(uint8_t gateway_id) {
|
||||
@@ -595,6 +759,86 @@ GatewayCacheDaliAddressState GatewayCache::daliAddressState(uint8_t gateway_id,
|
||||
return ensureDaliAddressStateLocked(gateway_id, short_address);
|
||||
}
|
||||
|
||||
GatewayCacheDaliPresence GatewayCache::daliAddressPresence(uint8_t gateway_id,
|
||||
uint8_t short_address) {
|
||||
LockGuard guard(lock_);
|
||||
if (short_address >= 64) {
|
||||
return GatewayCacheDaliPresence::kUnknown;
|
||||
}
|
||||
if (const auto it = dali_presence_.find(gateway_id); it != dali_presence_.end()) {
|
||||
return it->second[short_address];
|
||||
}
|
||||
return GatewayCacheDaliPresence::kUnknown;
|
||||
}
|
||||
|
||||
void GatewayCache::markDaliAddressPresence(uint8_t gateway_id, uint8_t short_address,
|
||||
GatewayCacheDaliPresence presence) {
|
||||
LockGuard guard(lock_);
|
||||
markDaliAddressPresenceLocked(gateway_id, short_address, presence);
|
||||
}
|
||||
|
||||
std::optional<GatewayCacheDaliTarget> GatewayCache::decodeDaliTarget(uint8_t raw_addr) {
|
||||
return DecodeDaliTarget(raw_addr);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GatewayCache::reconciliationAddresses(
|
||||
uint8_t gateway_id, std::optional<GatewayCacheDaliTarget> target) {
|
||||
LockGuard guard(lock_);
|
||||
std::vector<uint8_t> addresses;
|
||||
|
||||
auto presence = [&](uint8_t short_address) {
|
||||
if (const auto it = dali_presence_.find(gateway_id); it != dali_presence_.end()) {
|
||||
return it->second[short_address];
|
||||
}
|
||||
return GatewayCacheDaliPresence::kUnknown;
|
||||
};
|
||||
auto add_if_known_online = [&](uint8_t short_address) {
|
||||
if (short_address < 64 && presence(short_address) == GatewayCacheDaliPresence::kOnline) {
|
||||
addresses.push_back(short_address);
|
||||
}
|
||||
};
|
||||
|
||||
if (!target.has_value()) {
|
||||
for (uint8_t short_address = 0; short_address < 64; ++short_address) {
|
||||
add_if_known_online(short_address);
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
switch (target->kind) {
|
||||
case GatewayCacheDaliTargetKind::kShortAddress:
|
||||
if (target->value < 64 && presence(target->value) != GatewayCacheDaliPresence::kOffline) {
|
||||
addresses.push_back(target->value);
|
||||
}
|
||||
break;
|
||||
case GatewayCacheDaliTargetKind::kGroup: {
|
||||
if (target->value >= 16) {
|
||||
break;
|
||||
}
|
||||
const uint16_t bit = static_cast<uint16_t>(1U << target->value);
|
||||
auto [states_it, inserted] = dali_states_.try_emplace(gateway_id);
|
||||
if (inserted) {
|
||||
loadDaliStateStoreLocked(gateway_id, states_it->second);
|
||||
}
|
||||
for (uint8_t short_address = 0; short_address < states_it->second.size(); ++short_address) {
|
||||
const auto& state = states_it->second[short_address];
|
||||
if (state.group_mask_known && (state.group_mask & bit) != 0) {
|
||||
add_if_known_online(short_address);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GatewayCacheDaliTargetKind::kBroadcast:
|
||||
for (uint8_t short_address = 0; short_address < 64; ++short_address) {
|
||||
add_if_known_online(short_address);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
GatewayCacheDaliRuntimeStatus GatewayCache::daliGroupStatus(uint8_t gateway_id,
|
||||
uint8_t group_id) {
|
||||
LockGuard guard(lock_);
|
||||
@@ -620,6 +864,7 @@ bool GatewayCache::setDaliGroupMask(uint8_t gateway_id, uint8_t short_address,
|
||||
state.group_mask_known = group_mask.has_value();
|
||||
state.group_mask = group_mask.value_or(0);
|
||||
refreshDaliAddressAggregateStatusLocked(gateway_id, state);
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -632,6 +877,7 @@ bool GatewayCache::setDaliSceneLevel(uint8_t gateway_id, uint8_t short_address,
|
||||
|
||||
auto& state = ensureDaliAddressStateLocked(gateway_id, short_address);
|
||||
state.scene_levels[scene_id] = level;
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -644,6 +890,31 @@ bool GatewayCache::setDaliSettings(uint8_t gateway_id, uint8_t short_address,
|
||||
|
||||
auto& state = ensureDaliAddressStateLocked(gateway_id, short_address);
|
||||
state.settings = settings.value_or(GatewayCacheDaliSettingsSnapshot{});
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GatewayCache::setDaliActualLevel(uint8_t gateway_id, uint8_t short_address,
|
||||
std::optional<uint8_t> level) {
|
||||
LockGuard guard(lock_);
|
||||
if (short_address >= 64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GatewayCacheDaliRuntimeStatus status;
|
||||
status.actual_level = level;
|
||||
status.revision = nextDaliRuntimeRevisionLocked();
|
||||
status.stale = false;
|
||||
auto& state = ensureDaliAddressStateLocked(gateway_id, short_address);
|
||||
state.status.scene_id.reset();
|
||||
state.status.use_min_level = false;
|
||||
applyDaliRuntimeStatusToAddressLocked(state, status);
|
||||
if (!level.has_value()) {
|
||||
state.status.actual_level.reset();
|
||||
state.status.revision = status.revision;
|
||||
state.status.stale = false;
|
||||
}
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -712,7 +983,8 @@ bool GatewayCache::observeDaliCommand(uint8_t gateway_id, uint8_t raw_addr, uint
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ShouldMirrorObservedMutation(origin, priority_mode_)) {
|
||||
if (ShouldAlwaysMirrorObservedStatus(raw_addr, command) ||
|
||||
ShouldMirrorObservedMutation(origin, priority_mode_)) {
|
||||
mirrorDaliCommandLocked(gateway_id, raw_addr, command);
|
||||
}
|
||||
|
||||
@@ -791,12 +1063,15 @@ bool GatewayCache::mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr,
|
||||
GatewayCacheDaliRuntimeStatus status;
|
||||
status.actual_level = command;
|
||||
status.revision = nextDaliRuntimeRevisionLocked();
|
||||
status.stale = false;
|
||||
applyDaliTargetRuntimeStatusLocked(gateway_id, *target, status);
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (command == kDaliCmdReset) {
|
||||
clearDaliTargetStateLocked(gateway_id, *target, nextDaliRuntimeRevisionLocked());
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -804,7 +1079,9 @@ bool GatewayCache::mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr,
|
||||
GatewayCacheDaliRuntimeStatus status;
|
||||
status.actual_level = command == kDaliCmdOff ? 0 : 254;
|
||||
status.revision = nextDaliRuntimeRevisionLocked();
|
||||
status.stale = false;
|
||||
applyDaliTargetRuntimeStatusLocked(gateway_id, *target, status);
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -812,7 +1089,9 @@ bool GatewayCache::mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr,
|
||||
GatewayCacheDaliRuntimeStatus status;
|
||||
status.use_min_level = true;
|
||||
status.revision = nextDaliRuntimeRevisionLocked();
|
||||
status.stale = false;
|
||||
applyDaliTargetRuntimeStatusLocked(gateway_id, *target, status);
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -820,7 +1099,9 @@ bool GatewayCache::mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr,
|
||||
GatewayCacheDaliRuntimeStatus status;
|
||||
status.scene_id = static_cast<uint8_t>(command - kDaliCmdGoToSceneMin);
|
||||
status.revision = nextDaliRuntimeRevisionLocked();
|
||||
status.stale = false;
|
||||
applyDaliTargetRuntimeStatusLocked(gateway_id, *target, status);
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -828,6 +1109,7 @@ bool GatewayCache::mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr,
|
||||
applyDaliTargetGroupMutationLocked(gateway_id, *target,
|
||||
static_cast<uint8_t>(command & 0x0F),
|
||||
command < (kDaliCmdAddToGroupMin + 16));
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -836,6 +1118,7 @@ bool GatewayCache::mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr,
|
||||
applyDaliTargetSceneLevelLocked(gateway_id, *target,
|
||||
static_cast<uint8_t>(command - kDaliCmdSetSceneMin),
|
||||
*dtr_state.dtr0);
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -843,12 +1126,14 @@ bool GatewayCache::mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr,
|
||||
applyDaliTargetSceneLevelLocked(
|
||||
gateway_id, *target, static_cast<uint8_t>(command - (kDaliCmdSetSceneMin + 16)),
|
||||
static_cast<uint8_t>(255U));
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (command >= kDaliCmdStoreDtrAsMaxLevel && command <= kDaliCmdStoreDtrAsFadeRate &&
|
||||
dtr_state.dtr0.has_value()) {
|
||||
applyDaliTargetSettingsLocked(gateway_id, *target, command, *dtr_state.dtr0);
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -977,6 +1262,7 @@ void GatewayCache::applyDaliRuntimeStatusToAddressLocked(
|
||||
}
|
||||
}
|
||||
state.status.revision = status.revision;
|
||||
state.status.stale = status.stale;
|
||||
}
|
||||
|
||||
void GatewayCache::applyDaliTargetGroupMutationLocked(uint8_t gateway_id,
|
||||
@@ -1127,7 +1413,9 @@ void GatewayCache::refreshDaliAddressAggregateStatusLocked(uint8_t gateway_id,
|
||||
|
||||
if (const auto broadcast = dali_broadcast_status_.find(gateway_id);
|
||||
broadcast != dali_broadcast_status_.end()) {
|
||||
applyDaliRuntimeStatusToAddressLocked(state, broadcast->second);
|
||||
if (!broadcast->second.stale) {
|
||||
applyDaliRuntimeStatusToAddressLocked(state, broadcast->second);
|
||||
}
|
||||
}
|
||||
|
||||
const auto groups = dali_group_status_.find(gateway_id);
|
||||
@@ -1137,6 +1425,9 @@ void GatewayCache::refreshDaliAddressAggregateStatusLocked(uint8_t gateway_id,
|
||||
for (uint8_t group_id = 0; group_id < groups->second.size(); ++group_id) {
|
||||
const uint16_t bit = static_cast<uint16_t>(1U << group_id);
|
||||
if ((state.group_mask & bit) != 0) {
|
||||
if (groups->second[group_id].stale) {
|
||||
continue;
|
||||
}
|
||||
applyDaliRuntimeStatusToAddressLocked(state, groups->second[group_id]);
|
||||
}
|
||||
}
|
||||
@@ -1209,6 +1500,14 @@ bool GatewayCache::flushDirty() {
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [gateway_id, states] : dali_states_) {
|
||||
for (uint8_t short_address = 0; short_address < states.size(); ++short_address) {
|
||||
if (!persistDaliAddressStateLocked(gateway_id, short_address, states[short_address])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const esp_err_t commit_err = nvs_commit(storage_);
|
||||
if (commit_err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "cache commit failed: %s", esp_err_to_name(commit_err));
|
||||
@@ -1288,6 +1587,18 @@ bool GatewayCache::persistGroupLocked(uint8_t gateway_id, uint8_t group_id,
|
||||
return commitStorageLocked();
|
||||
}
|
||||
|
||||
bool GatewayCache::persistDaliAddressStateLocked(
|
||||
uint8_t gateway_id, uint8_t short_address, const GatewayCacheDaliAddressState& state) {
|
||||
if (short_address >= 64) {
|
||||
return false;
|
||||
}
|
||||
if (!IsDefaultDaliAddressState(state)) {
|
||||
return writeStringLocked(ShortKey("ds", gateway_id, short_address),
|
||||
BuildDaliStatePayload(state));
|
||||
}
|
||||
return eraseKeyLocked(ShortKey("ds", gateway_id, short_address));
|
||||
}
|
||||
|
||||
bool GatewayCache::commitStorageLocked() {
|
||||
if (storage_ == 0) {
|
||||
return false;
|
||||
@@ -1304,10 +1615,26 @@ bool GatewayCache::shouldTrackUpdateFlagsLocked() const {
|
||||
return config_.cache_enabled && config_.reconciliation_enabled;
|
||||
}
|
||||
|
||||
void GatewayCache::markDaliAddressPresenceLocked(uint8_t gateway_id, uint8_t short_address,
|
||||
GatewayCacheDaliPresence presence) {
|
||||
if (short_address >= 64) {
|
||||
return;
|
||||
}
|
||||
auto& states = dali_presence_[gateway_id];
|
||||
const auto previous = states[short_address];
|
||||
states[short_address] = presence;
|
||||
if (previous != presence) {
|
||||
ESP_LOGD(kTag, "presence gateway=%u short=%u state=%u", gateway_id, short_address,
|
||||
static_cast<unsigned>(presence));
|
||||
}
|
||||
}
|
||||
|
||||
GatewayCacheDaliAddressState& GatewayCache::ensureDaliAddressStateLocked(uint8_t gateway_id,
|
||||
uint8_t short_address) {
|
||||
auto [it, inserted] = dali_states_.try_emplace(gateway_id);
|
||||
(void)inserted;
|
||||
if (inserted) {
|
||||
loadDaliStateStoreLocked(gateway_id, it->second);
|
||||
}
|
||||
return it->second[short_address];
|
||||
}
|
||||
|
||||
@@ -1377,6 +1704,23 @@ void GatewayCache::loadGroupStoreLocked(uint8_t gateway_id, GroupStore& groups)
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayCache::loadDaliStateStoreLocked(
|
||||
uint8_t gateway_id, std::array<GatewayCacheDaliAddressState, 64>& states) {
|
||||
for (uint8_t short_address = 0; short_address < states.size(); ++short_address) {
|
||||
ClearDaliState(states[short_address]);
|
||||
const auto raw = readStringLocked(ShortKey("ds", gateway_id, short_address));
|
||||
if (!raw.empty()) {
|
||||
ApplyDaliStatePayload(raw, states[short_address]);
|
||||
if (states[short_address].status.anyKnown()) {
|
||||
states[short_address].status.stale = true;
|
||||
}
|
||||
if (states[short_address].status.revision > dali_runtime_revision_) {
|
||||
dali_runtime_revision_ = states[short_address].status.revision;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GatewayCache::readStringLocked(std::string_view key) {
|
||||
if (!openStorageLocked()) {
|
||||
return {};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
idf_component_register(
|
||||
SRCS "src/gateway_controller.cpp"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES dali_domain gateway_runtime gateway_cache freertos log
|
||||
REQUIRES dali_domain gateway_runtime gateway_cache gateway_bridge freertos log
|
||||
)
|
||||
|
||||
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -19,10 +20,11 @@ namespace gateway {
|
||||
|
||||
class DaliDomainService;
|
||||
struct DaliRawFrame;
|
||||
class GatewayBridgeService;
|
||||
class GatewayRuntime;
|
||||
|
||||
struct GatewayControllerConfig {
|
||||
uint32_t task_stack_size{6144};
|
||||
uint32_t task_stack_size{12288};
|
||||
UBaseType_t task_priority{5};
|
||||
int color_temperature_min{2000};
|
||||
int color_temperature_max{6500};
|
||||
@@ -32,6 +34,11 @@ struct GatewayControllerConfig {
|
||||
bool ip_router_supported{true};
|
||||
bool internal_scene_supported{true};
|
||||
bool internal_group_supported{true};
|
||||
bool cache_supported{true};
|
||||
uint32_t cache_refresh_interval_ms{120000};
|
||||
uint32_t cache_refresh_idle_ms{100};
|
||||
uint32_t cache_host_snooze_ms{5000};
|
||||
uint32_t cache_host_echo_ms{250};
|
||||
};
|
||||
|
||||
struct GatewayChannelSnapshot {
|
||||
@@ -76,6 +83,7 @@ class GatewayController {
|
||||
void addBleStateSink(BleStateSink sink);
|
||||
void addWifiStateSink(WifiStateSink sink);
|
||||
void addGatewayNameSink(GatewayNameSink sink);
|
||||
void setBridgeService(GatewayBridgeService* bridge_service);
|
||||
|
||||
bool setupMode() const;
|
||||
bool wirelessSetupMode() const;
|
||||
@@ -96,17 +104,35 @@ class GatewayController {
|
||||
|
||||
GatewayCacheChannelFlags flags{};
|
||||
Phase phase{Phase::kReloadFlags};
|
||||
uint8_t short_address{0};
|
||||
std::optional<GatewayCacheDaliTarget> target;
|
||||
std::vector<uint8_t> addresses;
|
||||
size_t address_index{0};
|
||||
uint8_t scene_id{0};
|
||||
};
|
||||
|
||||
struct BridgeTransportRequestState {
|
||||
uint8_t version{0};
|
||||
uint16_t payload_length{0};
|
||||
uint8_t total_chunks{0};
|
||||
std::map<uint8_t, std::vector<uint8_t>> chunks;
|
||||
};
|
||||
|
||||
struct CacheRefreshJob {
|
||||
TickType_t next_due_tick{0};
|
||||
uint8_t short_address{0};
|
||||
};
|
||||
|
||||
static void TaskEntry(void* arg);
|
||||
void taskLoop();
|
||||
void dispatchCommand(const std::vector<uint8_t>& command);
|
||||
void scheduleReconciliation(uint8_t gateway_id);
|
||||
void scheduleReconciliation(uint8_t gateway_id,
|
||||
std::optional<GatewayCacheDaliTarget> target = std::nullopt);
|
||||
bool hasPendingReconciliation() const;
|
||||
bool cacheRefreshEnabled() const;
|
||||
bool cacheMaintenanceSnoozed(uint8_t gateway_id) const;
|
||||
bool runMaintenanceStep();
|
||||
bool runReconciliationStep(uint8_t gateway_id, ReconciliationJob& job);
|
||||
bool runCacheRefreshStep();
|
||||
void reconcileGroupStep(uint8_t gateway_id, uint8_t short_address);
|
||||
void reconcileSceneStep(uint8_t gateway_id, uint8_t short_address, uint8_t scene_id);
|
||||
void reconcileSettingsStep(uint8_t gateway_id, uint8_t short_address);
|
||||
@@ -156,10 +182,15 @@ class GatewayController {
|
||||
void handleAllocationCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
||||
void handleInternalSceneCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
||||
void handleInternalGroupCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
||||
void handleGatewayCacheCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
||||
void handleBridgeTransportCommand(uint8_t gateway_id, const std::vector<uint8_t>& command);
|
||||
void publishBridgeTransportResponse(uint8_t gateway_id, uint8_t version, uint8_t sequence,
|
||||
std::string_view response);
|
||||
|
||||
GatewayRuntime& runtime_;
|
||||
DaliDomainService& dali_domain_;
|
||||
GatewayCache& cache_;
|
||||
GatewayBridgeService* bridge_service_{nullptr};
|
||||
GatewayControllerConfig config_;
|
||||
TaskHandle_t task_handle_{nullptr};
|
||||
SemaphoreHandle_t maintenance_lock_{nullptr};
|
||||
@@ -167,7 +198,9 @@ class GatewayController {
|
||||
std::vector<BleStateSink> ble_state_sinks_;
|
||||
std::vector<WifiStateSink> wifi_state_sinks_;
|
||||
std::vector<GatewayNameSink> gateway_name_sinks_;
|
||||
std::map<uint16_t, BridgeTransportRequestState> bridge_transport_requests_;
|
||||
std::map<uint8_t, ReconciliationJob> reconciliation_jobs_;
|
||||
std::map<uint8_t, CacheRefreshJob> cache_refresh_jobs_;
|
||||
std::atomic<int> maintenance_activity_gateway_{-1};
|
||||
bool setup_mode_{false};
|
||||
bool wireless_setup_mode_{false};
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
#include "dali_domain.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "gateway_bridge.hpp"
|
||||
#include "gateway_runtime.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace gateway {
|
||||
@@ -21,6 +23,38 @@ constexpr uint8_t kDaliSceneCount = 16;
|
||||
constexpr uint8_t kDaliCmdOff = 0x00;
|
||||
constexpr uint8_t kDaliCmdRecallMax = 0x05;
|
||||
constexpr TickType_t kMaintenancePollTicks = pdMS_TO_TICKS(20);
|
||||
constexpr TickType_t kIdleMaintenancePollTicks = pdMS_TO_TICKS(1000);
|
||||
constexpr uint8_t kBridgeTransportRequestOpcode = 0xB0;
|
||||
constexpr uint8_t kBridgeTransportResponseOpcode = 0xB1;
|
||||
constexpr uint8_t kBridgeTransportVersion = 1;
|
||||
constexpr size_t kBridgeTransportMaxChunkBytes = 120;
|
||||
constexpr uint8_t kGatewayFeatureCache = 0x40;
|
||||
constexpr uint8_t kGatewayCacheOpcode = 0x39;
|
||||
constexpr uint8_t kGatewayCacheProtocolVersion = 1;
|
||||
constexpr uint8_t kGatewayCacheOpSummary = 0x00;
|
||||
constexpr uint8_t kGatewayCacheOpShortAddress = 0x01;
|
||||
constexpr uint8_t kGatewayCacheOpGroup = 0x02;
|
||||
constexpr uint8_t kGatewayCacheOpBroadcast = 0x03;
|
||||
constexpr uint8_t kGatewayCacheStatusOk = 0x00;
|
||||
constexpr uint8_t kGatewayCacheStatusDisabled = 0x01;
|
||||
constexpr uint8_t kGatewayCacheStatusInvalidArgument = 0x02;
|
||||
constexpr uint16_t kCacheFlagActualKnown = 1U << 0;
|
||||
constexpr uint16_t kCacheFlagSceneKnown = 1U << 1;
|
||||
constexpr uint16_t kCacheFlagUseMinLevel = 1U << 2;
|
||||
constexpr uint16_t kCacheFlagStatusStale = 1U << 3;
|
||||
constexpr uint16_t kCacheFlagGroupMaskKnown = 1U << 4;
|
||||
constexpr uint16_t kCacheFlagPowerOnKnown = 1U << 5;
|
||||
constexpr uint16_t kCacheFlagSystemFailureKnown = 1U << 6;
|
||||
constexpr uint16_t kCacheFlagMinKnown = 1U << 7;
|
||||
constexpr uint16_t kCacheFlagMaxKnown = 1U << 8;
|
||||
constexpr uint16_t kCacheFlagFadeTimeKnown = 1U << 9;
|
||||
constexpr uint16_t kCacheFlagFadeRateKnown = 1U << 10;
|
||||
constexpr const char* kBridgeTransportInvalidFrameResponse =
|
||||
"{\"statusCode\":400,\"error\":\"invalid bridge transport frame\","
|
||||
"\"message\":\"invalid bridge transport frame\"}";
|
||||
constexpr const char* kBridgeTransportUnavailableResponse =
|
||||
"{\"statusCode\":500,\"error\":\"bridge service is not enabled\","
|
||||
"\"message\":\"bridge service is not enabled\"}";
|
||||
|
||||
class LockGuard {
|
||||
public:
|
||||
@@ -44,6 +78,43 @@ bool AnyFlagSet(const GatewayCacheChannelFlags& flags) {
|
||||
return flags.need_update_group || flags.need_update_scene || flags.need_update_settings;
|
||||
}
|
||||
|
||||
bool SameTarget(const std::optional<GatewayCacheDaliTarget>& lhs,
|
||||
const std::optional<GatewayCacheDaliTarget>& rhs) {
|
||||
if (lhs.has_value() != rhs.has_value()) {
|
||||
return false;
|
||||
}
|
||||
if (!lhs.has_value()) {
|
||||
return true;
|
||||
}
|
||||
return lhs->kind == rhs->kind && lhs->value == rhs->value;
|
||||
}
|
||||
|
||||
bool IsDaliHostCommandOpcode(uint8_t opcode) {
|
||||
switch (opcode) {
|
||||
case 0x07:
|
||||
case 0x08:
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
case 0x14:
|
||||
case 0x15:
|
||||
case 0x16:
|
||||
case 0x17:
|
||||
case 0x18:
|
||||
case 0x30:
|
||||
case 0x32:
|
||||
case 0x37:
|
||||
case 0x38:
|
||||
case 0xA0:
|
||||
case 0xA2:
|
||||
case kBridgeTransportRequestOpcode:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string NormalizeName(std::string_view name) {
|
||||
std::string normalized(name);
|
||||
if (normalized.size() > kMaxNameBytes) {
|
||||
@@ -73,6 +144,79 @@ void AppendStringBytes(std::vector<uint8_t>& out, std::string_view value) {
|
||||
}
|
||||
}
|
||||
|
||||
void AppendLe16(std::vector<uint8_t>& out, uint16_t value) {
|
||||
out.push_back(static_cast<uint8_t>(value & 0xFF));
|
||||
out.push_back(static_cast<uint8_t>((value >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
void AppendLe32(std::vector<uint8_t>& out, uint32_t value) {
|
||||
out.push_back(static_cast<uint8_t>(value & 0xFF));
|
||||
out.push_back(static_cast<uint8_t>((value >> 8) & 0xFF));
|
||||
out.push_back(static_cast<uint8_t>((value >> 16) & 0xFF));
|
||||
out.push_back(static_cast<uint8_t>((value >> 24) & 0xFF));
|
||||
}
|
||||
|
||||
uint8_t CacheByte(std::optional<uint8_t> value) {
|
||||
return value.value_or(0xFF);
|
||||
}
|
||||
|
||||
uint16_t CacheRuntimeFlags(const GatewayCacheDaliRuntimeStatus& status) {
|
||||
uint16_t flags = 0;
|
||||
if (status.actual_level.has_value()) {
|
||||
flags |= kCacheFlagActualKnown;
|
||||
}
|
||||
if (status.scene_id.has_value()) {
|
||||
flags |= kCacheFlagSceneKnown;
|
||||
}
|
||||
if (status.use_min_level) {
|
||||
flags |= kCacheFlagUseMinLevel;
|
||||
}
|
||||
if (status.stale) {
|
||||
flags |= kCacheFlagStatusStale;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
uint16_t CacheAddressFlags(const GatewayCacheDaliAddressState& state) {
|
||||
uint16_t flags = CacheRuntimeFlags(state.status);
|
||||
if (state.group_mask_known) {
|
||||
flags |= kCacheFlagGroupMaskKnown;
|
||||
}
|
||||
if (state.settings.power_on_level.has_value()) {
|
||||
flags |= kCacheFlagPowerOnKnown;
|
||||
}
|
||||
if (state.settings.system_failure_level.has_value()) {
|
||||
flags |= kCacheFlagSystemFailureKnown;
|
||||
}
|
||||
if (state.settings.min_level.has_value()) {
|
||||
flags |= kCacheFlagMinKnown;
|
||||
}
|
||||
if (state.settings.max_level.has_value()) {
|
||||
flags |= kCacheFlagMaxKnown;
|
||||
}
|
||||
if (state.settings.fade_time.has_value()) {
|
||||
flags |= kCacheFlagFadeTimeKnown;
|
||||
}
|
||||
if (state.settings.fade_rate.has_value()) {
|
||||
flags |= kCacheFlagFadeRateKnown;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
uint16_t CacheSceneKnownMask(const GatewayCacheDaliAddressState& state) {
|
||||
uint16_t mask = 0;
|
||||
for (size_t index = 0; index < state.scene_levels.size(); ++index) {
|
||||
if (state.scene_levels[index].has_value()) {
|
||||
mask |= static_cast<uint16_t>(1U << index);
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
uint16_t BridgeTransportRequestKey(uint8_t gateway_id, uint8_t sequence) {
|
||||
return static_cast<uint16_t>((static_cast<uint16_t>(gateway_id) << 8) | sequence);
|
||||
}
|
||||
|
||||
void AppendPaddedName(std::vector<uint8_t>& out, std::string_view name) {
|
||||
const auto normalized = NormalizeName(name);
|
||||
out.push_back(static_cast<uint8_t>(normalized.size()));
|
||||
@@ -185,6 +329,10 @@ void GatewayController::addGatewayNameSink(GatewayNameSink sink) {
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayController::setBridgeService(GatewayBridgeService* bridge_service) {
|
||||
bridge_service_ = bridge_service;
|
||||
}
|
||||
|
||||
bool GatewayController::setupMode() const {
|
||||
return setup_mode_;
|
||||
}
|
||||
@@ -256,12 +404,17 @@ void GatewayController::taskLoop() {
|
||||
worked = runMaintenanceStep();
|
||||
}
|
||||
if (!worked) {
|
||||
ulTaskNotifyTake(pdTRUE, hasPendingReconciliation() ? kMaintenancePollTicks : portMAX_DELAY);
|
||||
const TickType_t wait_ticks = hasPendingReconciliation()
|
||||
? kMaintenancePollTicks
|
||||
: cacheRefreshEnabled() ? kIdleMaintenancePollTicks
|
||||
: portMAX_DELAY;
|
||||
ulTaskNotifyTake(pdTRUE, wait_ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayController::scheduleReconciliation(uint8_t gateway_id) {
|
||||
void GatewayController::scheduleReconciliation(uint8_t gateway_id,
|
||||
std::optional<GatewayCacheDaliTarget> target) {
|
||||
if (!cache_.reconciliationEnabled()) {
|
||||
return;
|
||||
}
|
||||
@@ -276,7 +429,16 @@ void GatewayController::scheduleReconciliation(uint8_t gateway_id) {
|
||||
|
||||
{
|
||||
LockGuard guard(maintenance_lock_);
|
||||
reconciliation_jobs_.try_emplace(gateway_id);
|
||||
auto [it, inserted] = reconciliation_jobs_.try_emplace(gateway_id);
|
||||
if (inserted) {
|
||||
it->second.target = target;
|
||||
} else if (!SameTarget(it->second.target, target)) {
|
||||
it->second.target.reset();
|
||||
it->second.phase = ReconciliationJob::Phase::kReloadFlags;
|
||||
it->second.addresses.clear();
|
||||
it->second.address_index = 0;
|
||||
it->second.scene_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (task_handle_ != nullptr) {
|
||||
@@ -289,42 +451,58 @@ bool GatewayController::hasPendingReconciliation() const {
|
||||
return !reconciliation_jobs_.empty();
|
||||
}
|
||||
|
||||
bool GatewayController::cacheRefreshEnabled() const {
|
||||
return config_.cache_supported && cache_.cacheEnabled() &&
|
||||
config_.cache_refresh_interval_ms > 0;
|
||||
}
|
||||
|
||||
bool GatewayController::cacheMaintenanceSnoozed(uint8_t gateway_id) const {
|
||||
return config_.cache_host_snooze_ms > 0 &&
|
||||
dali_domain_.hasRecentHostActivity(gateway_id, config_.cache_host_snooze_ms);
|
||||
}
|
||||
|
||||
bool GatewayController::runMaintenanceStep() {
|
||||
if (!cache_.reconciliationEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t gateway_id = 0;
|
||||
ReconciliationJob job;
|
||||
{
|
||||
LockGuard guard(maintenance_lock_);
|
||||
if (reconciliation_jobs_.empty()) {
|
||||
return false;
|
||||
if (cache_.reconciliationEnabled()) {
|
||||
bool has_job = false;
|
||||
uint8_t gateway_id = 0;
|
||||
ReconciliationJob job;
|
||||
{
|
||||
LockGuard guard(maintenance_lock_);
|
||||
if (!reconciliation_jobs_.empty()) {
|
||||
const auto it = reconciliation_jobs_.begin();
|
||||
has_job = true;
|
||||
gateway_id = it->first;
|
||||
job = it->second;
|
||||
}
|
||||
}
|
||||
const auto it = reconciliation_jobs_.begin();
|
||||
gateway_id = it->first;
|
||||
job = it->second;
|
||||
}
|
||||
|
||||
if (runtime_.shouldYieldMaintenance(gateway_id)) {
|
||||
return false;
|
||||
}
|
||||
if (has_job) {
|
||||
if (runtime_.shouldYieldMaintenance(gateway_id)) {
|
||||
return false;
|
||||
}
|
||||
if (cacheMaintenanceSnoozed(gateway_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool keep_job = runReconciliationStep(gateway_id, job);
|
||||
const bool keep_job = runReconciliationStep(gateway_id, job);
|
||||
|
||||
{
|
||||
LockGuard guard(maintenance_lock_);
|
||||
auto it = reconciliation_jobs_.find(gateway_id);
|
||||
if (it == reconciliation_jobs_.end()) {
|
||||
{
|
||||
LockGuard guard(maintenance_lock_);
|
||||
auto it = reconciliation_jobs_.find(gateway_id);
|
||||
if (it == reconciliation_jobs_.end()) {
|
||||
return true;
|
||||
}
|
||||
if (keep_job) {
|
||||
it->second = job;
|
||||
} else {
|
||||
reconciliation_jobs_.erase(it);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (keep_job) {
|
||||
it->second = job;
|
||||
} else {
|
||||
reconciliation_jobs_.erase(it);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
return runCacheRefreshStep();
|
||||
}
|
||||
|
||||
bool GatewayController::runReconciliationStep(uint8_t gateway_id, ReconciliationJob& job) {
|
||||
@@ -337,8 +515,13 @@ bool GatewayController::runReconciliationStep(uint8_t gateway_id, Reconciliation
|
||||
return false;
|
||||
}
|
||||
|
||||
job.short_address = 0;
|
||||
job.addresses = cache_.reconciliationAddresses(gateway_id, job.target);
|
||||
job.address_index = 0;
|
||||
job.scene_id = 0;
|
||||
if (job.addresses.empty()) {
|
||||
cache_.clearChannelFlagsIfMatched(gateway_id, job.flags);
|
||||
return false;
|
||||
}
|
||||
if (job.flags.need_update_group) {
|
||||
job.phase = ReconciliationJob::Phase::kGroups;
|
||||
} else if (job.flags.need_update_scene) {
|
||||
@@ -350,9 +533,9 @@ bool GatewayController::runReconciliationStep(uint8_t gateway_id, Reconciliation
|
||||
|
||||
switch (job.phase) {
|
||||
case ReconciliationJob::Phase::kGroups:
|
||||
reconcileGroupStep(gateway_id, job.short_address++);
|
||||
if (job.short_address >= kDaliShortAddressCount) {
|
||||
job.short_address = 0;
|
||||
reconcileGroupStep(gateway_id, job.addresses[job.address_index++]);
|
||||
if (job.address_index >= job.addresses.size()) {
|
||||
job.address_index = 0;
|
||||
if (job.flags.need_update_scene) {
|
||||
job.phase = ReconciliationJob::Phase::kScenes;
|
||||
} else if (job.flags.need_update_settings) {
|
||||
@@ -364,15 +547,22 @@ bool GatewayController::runReconciliationStep(uint8_t gateway_id, Reconciliation
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case ReconciliationJob::Phase::kScenes:
|
||||
reconcileSceneStep(gateway_id, job.short_address, job.scene_id);
|
||||
++job.scene_id;
|
||||
case ReconciliationJob::Phase::kScenes: {
|
||||
const uint8_t short_address = job.addresses[job.address_index];
|
||||
if (cache_.daliAddressPresence(gateway_id, short_address) ==
|
||||
GatewayCacheDaliPresence::kOffline) {
|
||||
job.scene_id = 0;
|
||||
++job.address_index;
|
||||
} else {
|
||||
reconcileSceneStep(gateway_id, short_address, job.scene_id);
|
||||
++job.scene_id;
|
||||
}
|
||||
if (job.scene_id >= kDaliSceneCount) {
|
||||
job.scene_id = 0;
|
||||
++job.short_address;
|
||||
++job.address_index;
|
||||
}
|
||||
if (job.short_address >= kDaliShortAddressCount) {
|
||||
job.short_address = 0;
|
||||
if (job.address_index >= job.addresses.size()) {
|
||||
job.address_index = 0;
|
||||
if (job.flags.need_update_settings) {
|
||||
job.phase = ReconciliationJob::Phase::kSettings;
|
||||
} else if (!cache_.clearChannelFlagsIfMatched(gateway_id, job.flags)) {
|
||||
@@ -382,10 +572,15 @@ bool GatewayController::runReconciliationStep(uint8_t gateway_id, Reconciliation
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case ReconciliationJob::Phase::kSettings:
|
||||
reconcileSettingsStep(gateway_id, job.short_address++);
|
||||
if (job.short_address >= kDaliShortAddressCount) {
|
||||
job.short_address = 0;
|
||||
}
|
||||
case ReconciliationJob::Phase::kSettings: {
|
||||
const uint8_t short_address = job.addresses[job.address_index++];
|
||||
if (cache_.daliAddressPresence(gateway_id, short_address) !=
|
||||
GatewayCacheDaliPresence::kOffline) {
|
||||
reconcileSettingsStep(gateway_id, short_address);
|
||||
}
|
||||
if (job.address_index >= job.addresses.size()) {
|
||||
job.address_index = 0;
|
||||
if (!cache_.clearChannelFlagsIfMatched(gateway_id, job.flags)) {
|
||||
job.phase = ReconciliationJob::Phase::kReloadFlags;
|
||||
} else {
|
||||
@@ -393,12 +588,70 @@ bool GatewayController::runReconciliationStep(uint8_t gateway_id, Reconciliation
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case ReconciliationJob::Phase::kReloadFlags:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GatewayController::runCacheRefreshStep() {
|
||||
if (!cacheRefreshEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const TickType_t interval_ticks =
|
||||
std::max<TickType_t>(1, pdMS_TO_TICKS(config_.cache_refresh_interval_ms));
|
||||
const auto channels = dali_domain_.channelInfo();
|
||||
const TickType_t now = xTaskGetTickCount();
|
||||
|
||||
for (const auto& channel : channels) {
|
||||
auto& job = cache_refresh_jobs_[channel.gateway_id];
|
||||
if (job.next_due_tick == 0) {
|
||||
job.next_due_tick = now + interval_ticks;
|
||||
continue;
|
||||
}
|
||||
if (now < job.next_due_tick) {
|
||||
continue;
|
||||
}
|
||||
if (runtime_.shouldYieldMaintenance(channel.gateway_id) ||
|
||||
dali_domain_.isAllocAddr(channel.gateway_id) ||
|
||||
cacheMaintenanceSnoozed(channel.gateway_id) ||
|
||||
!dali_domain_.isBusIdle(channel.gateway_id, config_.cache_refresh_idle_ms)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto advance_job = [&]() {
|
||||
++job.short_address;
|
||||
if (job.short_address >= kDaliShortAddressCount) {
|
||||
job.short_address = 0;
|
||||
job.next_due_tick = xTaskGetTickCount() + interval_ticks;
|
||||
} else {
|
||||
job.next_due_tick = xTaskGetTickCount();
|
||||
}
|
||||
};
|
||||
|
||||
if (cache_.daliAddressPresence(channel.gateway_id, job.short_address) ==
|
||||
GatewayCacheDaliPresence::kOffline) {
|
||||
advance_job();
|
||||
return true;
|
||||
}
|
||||
|
||||
maintenance_activity_gateway_.store(channel.gateway_id);
|
||||
const auto actual_level = dali_domain_.queryActualLevel(channel.gateway_id, job.short_address);
|
||||
maintenance_activity_gateway_.store(-1);
|
||||
cache_.markDaliAddressPresence(channel.gateway_id, job.short_address,
|
||||
actual_level.has_value()
|
||||
? GatewayCacheDaliPresence::kOnline
|
||||
: GatewayCacheDaliPresence::kOffline);
|
||||
cache_.setDaliActualLevel(channel.gateway_id, job.short_address, actual_level);
|
||||
advance_job();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GatewayController::reconcileGroupStep(uint8_t gateway_id, uint8_t short_address) {
|
||||
const auto policy = cache_.priorityMode();
|
||||
const auto state = cache_.daliAddressState(gateway_id, short_address);
|
||||
@@ -408,6 +661,10 @@ void GatewayController::reconcileGroupStep(uint8_t gateway_id, uint8_t short_add
|
||||
const bool applied = dali_domain_.applyGroupMask(gateway_id, short_address, state.group_mask);
|
||||
maintenance_activity_gateway_.store(-1);
|
||||
const auto verified_mask = dali_domain_.queryGroupMask(gateway_id, short_address);
|
||||
cache_.markDaliAddressPresence(gateway_id, short_address,
|
||||
verified_mask.has_value()
|
||||
? GatewayCacheDaliPresence::kOnline
|
||||
: GatewayCacheDaliPresence::kOffline);
|
||||
cache_.setDaliGroupMask(gateway_id, short_address, verified_mask);
|
||||
if (!applied && verified_mask.has_value()) {
|
||||
ESP_LOGW(kTag, "group reconcile fallback gateway=%u short=%u", gateway_id, short_address);
|
||||
@@ -415,8 +672,11 @@ void GatewayController::reconcileGroupStep(uint8_t gateway_id, uint8_t short_add
|
||||
return;
|
||||
}
|
||||
|
||||
cache_.setDaliGroupMask(gateway_id, short_address,
|
||||
dali_domain_.queryGroupMask(gateway_id, short_address));
|
||||
const auto group_mask = dali_domain_.queryGroupMask(gateway_id, short_address);
|
||||
cache_.markDaliAddressPresence(gateway_id, short_address,
|
||||
group_mask.has_value() ? GatewayCacheDaliPresence::kOnline
|
||||
: GatewayCacheDaliPresence::kOffline);
|
||||
cache_.setDaliGroupMask(gateway_id, short_address, group_mask);
|
||||
}
|
||||
|
||||
void GatewayController::reconcileSceneStep(uint8_t gateway_id, uint8_t short_address,
|
||||
@@ -431,8 +691,11 @@ void GatewayController::reconcileSceneStep(uint8_t gateway_id, uint8_t short_add
|
||||
maintenance_activity_gateway_.store(-1);
|
||||
}
|
||||
|
||||
cache_.setDaliSceneLevel(gateway_id, short_address, scene_id,
|
||||
dali_domain_.querySceneLevel(gateway_id, short_address, scene_id));
|
||||
const auto level = dali_domain_.querySceneLevel(gateway_id, short_address, scene_id);
|
||||
cache_.markDaliAddressPresence(gateway_id, short_address,
|
||||
level.has_value() ? GatewayCacheDaliPresence::kOnline
|
||||
: GatewayCacheDaliPresence::kOffline);
|
||||
cache_.setDaliSceneLevel(gateway_id, short_address, scene_id, level);
|
||||
}
|
||||
|
||||
void GatewayController::reconcileSettingsStep(uint8_t gateway_id, uint8_t short_address) {
|
||||
@@ -453,6 +716,9 @@ void GatewayController::reconcileSettingsStep(uint8_t gateway_id, uint8_t short_
|
||||
}
|
||||
|
||||
const auto settings = dali_domain_.queryAddressSettings(gateway_id, short_address);
|
||||
cache_.markDaliAddressPresence(gateway_id, short_address,
|
||||
settings.has_value() ? GatewayCacheDaliPresence::kOnline
|
||||
: GatewayCacheDaliPresence::kOffline);
|
||||
if (settings.has_value()) {
|
||||
cache_.setDaliSettings(gateway_id, short_address,
|
||||
GatewayCacheDaliSettingsSnapshot{settings->power_on_level,
|
||||
@@ -476,10 +742,21 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
|
||||
const uint8_t opcode = command[3];
|
||||
const uint8_t addr = command[4];
|
||||
const uint8_t data = command[5];
|
||||
if (opcode == 0x09 && addr == 0x00) {
|
||||
const auto ids = gatewayIds();
|
||||
const auto count = std::min<size_t>(ids.size(), 16);
|
||||
std::vector<uint8_t> payload{0x09, static_cast<uint8_t>(count)};
|
||||
payload.insert(payload.end(), ids.begin(), ids.begin() + count);
|
||||
publishPayload(gateway_id, payload);
|
||||
return;
|
||||
}
|
||||
if (!hasGateway(gateway_id)) {
|
||||
ESP_LOGW(kTag, "command for unknown gateway=%u opcode=0x%02x", gateway_id, opcode);
|
||||
return;
|
||||
}
|
||||
if (IsDaliHostCommandOpcode(opcode)) {
|
||||
dali_domain_.markHostActivity(gateway_id);
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case 0x00:
|
||||
@@ -555,6 +832,9 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
|
||||
if (config_.internal_group_supported) {
|
||||
feature |= 0x20;
|
||||
}
|
||||
if (config_.cache_supported) {
|
||||
feature |= kGatewayFeatureCache;
|
||||
}
|
||||
publishPayload(gateway_id, {0x03, gateway_id, feature});
|
||||
break;
|
||||
}
|
||||
@@ -653,12 +933,18 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kGatewayCacheOpcode:
|
||||
handleGatewayCacheCommand(gateway_id, command);
|
||||
break;
|
||||
case 0xA0:
|
||||
handleInternalSceneCommand(gateway_id, command);
|
||||
break;
|
||||
case 0xA2:
|
||||
handleInternalGroupCommand(gateway_id, command);
|
||||
break;
|
||||
case kBridgeTransportRequestOpcode:
|
||||
handleBridgeTransportCommand(gateway_id, command);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(kTag, "unhandled opcode=0x%02x gateway=%u", opcode, gateway_id);
|
||||
break;
|
||||
@@ -714,12 +1000,120 @@ void GatewayController::publishPayload(uint8_t, const std::vector<uint8_t>& payl
|
||||
publishFrame(GatewayRuntime::buildNotificationFrame(payload));
|
||||
}
|
||||
|
||||
void GatewayController::publishBridgeTransportResponse(uint8_t gateway_id, uint8_t version,
|
||||
uint8_t sequence,
|
||||
std::string_view response) {
|
||||
const size_t total_chunks =
|
||||
std::max<size_t>(1, (response.size() + kBridgeTransportMaxChunkBytes - 1) /
|
||||
kBridgeTransportMaxChunkBytes);
|
||||
for (size_t index = 0; index < total_chunks; ++index) {
|
||||
const size_t start = index * kBridgeTransportMaxChunkBytes;
|
||||
const size_t chunk_length =
|
||||
std::min(kBridgeTransportMaxChunkBytes, response.size() - start);
|
||||
std::vector<uint8_t> payload{
|
||||
kBridgeTransportResponseOpcode,
|
||||
gateway_id,
|
||||
version,
|
||||
sequence,
|
||||
static_cast<uint8_t>(total_chunks),
|
||||
static_cast<uint8_t>(index),
|
||||
static_cast<uint8_t>(response.size() & 0xFF),
|
||||
static_cast<uint8_t>((response.size() >> 8) & 0xFF),
|
||||
static_cast<uint8_t>(chunk_length & 0xFF),
|
||||
static_cast<uint8_t>((chunk_length >> 8) & 0xFF),
|
||||
};
|
||||
payload.reserve(payload.size() + chunk_length);
|
||||
for (size_t offset = 0; offset < chunk_length; ++offset) {
|
||||
payload.push_back(static_cast<uint8_t>(response[start + offset]));
|
||||
}
|
||||
publishPayload(gateway_id, payload);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayController::publishFrame(const std::vector<uint8_t>& frame) {
|
||||
for (const auto& sink : notification_sinks_) {
|
||||
sink(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayController::handleBridgeTransportCommand(uint8_t gateway_id,
|
||||
const std::vector<uint8_t>& command) {
|
||||
const uint8_t version = command.size() > 4 ? command[4] : kBridgeTransportVersion;
|
||||
const uint8_t sequence = command.size() > 5 ? command[5] : 0;
|
||||
const uint16_t request_key = BridgeTransportRequestKey(gateway_id, sequence);
|
||||
if (command.size() < 11) {
|
||||
bridge_transport_requests_.erase(request_key);
|
||||
publishBridgeTransportResponse(gateway_id, version, sequence,
|
||||
kBridgeTransportInvalidFrameResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t total_chunks = command[6];
|
||||
const uint8_t chunk_index = command[7];
|
||||
const uint16_t payload_length =
|
||||
static_cast<uint16_t>(command[8] | (static_cast<uint16_t>(command[9]) << 8));
|
||||
if (version != kBridgeTransportVersion || total_chunks == 0 || chunk_index >= total_chunks) {
|
||||
bridge_transport_requests_.erase(request_key);
|
||||
publishBridgeTransportResponse(gateway_id, version, sequence,
|
||||
kBridgeTransportInvalidFrameResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& state = bridge_transport_requests_[request_key];
|
||||
if (chunk_index == 0 || state.version != version || state.payload_length != payload_length ||
|
||||
state.total_chunks != total_chunks) {
|
||||
state = BridgeTransportRequestState{};
|
||||
state.version = version;
|
||||
state.payload_length = payload_length;
|
||||
state.total_chunks = total_chunks;
|
||||
}
|
||||
|
||||
const size_t payload_start = 10;
|
||||
const size_t payload_end = command.size() - 1;
|
||||
if (payload_end < payload_start) {
|
||||
bridge_transport_requests_.erase(request_key);
|
||||
publishBridgeTransportResponse(gateway_id, version, sequence,
|
||||
kBridgeTransportInvalidFrameResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.chunks.find(chunk_index) == state.chunks.end()) {
|
||||
state.chunks[chunk_index] =
|
||||
std::vector<uint8_t>(command.begin() + payload_start, command.begin() + payload_end);
|
||||
}
|
||||
if (state.chunks.size() < total_chunks) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> request_bytes;
|
||||
request_bytes.reserve(payload_length);
|
||||
for (uint8_t index = 0; index < total_chunks; ++index) {
|
||||
const auto it = state.chunks.find(index);
|
||||
if (it == state.chunks.end()) {
|
||||
bridge_transport_requests_.erase(request_key);
|
||||
publishBridgeTransportResponse(gateway_id, version, sequence,
|
||||
kBridgeTransportInvalidFrameResponse);
|
||||
return;
|
||||
}
|
||||
request_bytes.insert(request_bytes.end(), it->second.begin(), it->second.end());
|
||||
}
|
||||
bridge_transport_requests_.erase(request_key);
|
||||
if (request_bytes.size() != payload_length) {
|
||||
publishBridgeTransportResponse(gateway_id, version, sequence,
|
||||
kBridgeTransportInvalidFrameResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string response =
|
||||
bridge_service_ == nullptr
|
||||
? std::string(kBridgeTransportUnavailableResponse)
|
||||
: bridge_service_->handleTransportRequest(
|
||||
gateway_id,
|
||||
std::string_view(reinterpret_cast<const char*>(request_bytes.data()),
|
||||
request_bytes.size()));
|
||||
publishBridgeTransportResponse(gateway_id, version, sequence, response);
|
||||
}
|
||||
|
||||
void GatewayController::handleDaliRawFrame(const DaliRawFrame& frame) {
|
||||
if (frame.data.size() != 2 && frame.data.size() != 3) {
|
||||
return;
|
||||
@@ -739,14 +1133,18 @@ void GatewayController::handleDaliRawFrame(const DaliRawFrame& frame) {
|
||||
}
|
||||
|
||||
const bool maintenance_activity = maintenance_activity_gateway_.load() == frame.gateway_id;
|
||||
const bool host_echo_activity =
|
||||
dali_domain_.matchesRecentHostCommandFrame(frame.gateway_id, addr, data,
|
||||
config_.cache_host_echo_ms) ||
|
||||
dali_domain_.hasRecentHostActivity(frame.gateway_id, config_.cache_host_echo_ms);
|
||||
const bool local_activity = maintenance_activity || runtime_.hasActiveCommand(frame.gateway_id) ||
|
||||
dali_domain_.isAllocAddr(frame.gateway_id);
|
||||
host_echo_activity || dali_domain_.isAllocAddr(frame.gateway_id);
|
||||
const bool flagged = cache_.observeDaliCommand(frame.gateway_id, addr, data,
|
||||
local_activity
|
||||
? GatewayCacheRawFrameOrigin::kLocalGateway
|
||||
: GatewayCacheRawFrameOrigin::kOutsideBus);
|
||||
if (flagged) {
|
||||
scheduleReconciliation(frame.gateway_id);
|
||||
scheduleReconciliation(frame.gateway_id, cache_.decodeDaliTarget(addr));
|
||||
}
|
||||
|
||||
if (setup_mode_ || dali_domain_.isAllocAddr(frame.gateway_id) || maintenance_activity ||
|
||||
@@ -775,26 +1173,31 @@ bool GatewayController::sendExtRawAndMirror(uint8_t gateway_id, uint8_t raw_addr
|
||||
}
|
||||
|
||||
bool GatewayController::setBrightAndMirror(uint8_t gateway_id, int dec_address, uint8_t level) {
|
||||
const uint8_t raw_addr = rawArcAddressFromDec(dec_address);
|
||||
dali_domain_.markHostCommandFrame(gateway_id, raw_addr, level);
|
||||
const bool sent = dali_domain_.setBright(gateway_id, dec_address, level);
|
||||
if (sent) {
|
||||
cache_.mirrorDaliCommand(gateway_id, rawArcAddressFromDec(dec_address), level);
|
||||
cache_.mirrorDaliCommand(gateway_id, raw_addr, level);
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
bool GatewayController::offAndMirror(uint8_t gateway_id, int dec_address) {
|
||||
const uint8_t raw_addr = rawCommandAddressFromDec(dec_address);
|
||||
dali_domain_.markHostCommandFrame(gateway_id, raw_addr, kDaliCmdOff);
|
||||
const bool sent = dali_domain_.off(gateway_id, dec_address);
|
||||
if (sent) {
|
||||
cache_.mirrorDaliCommand(gateway_id, rawCommandAddressFromDec(dec_address), kDaliCmdOff);
|
||||
cache_.mirrorDaliCommand(gateway_id, raw_addr, kDaliCmdOff);
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
bool GatewayController::onAndMirror(uint8_t gateway_id, int dec_address) {
|
||||
const uint8_t raw_addr = rawCommandAddressFromDec(dec_address);
|
||||
dali_domain_.markHostCommandFrame(gateway_id, raw_addr, kDaliCmdRecallMax);
|
||||
const bool sent = dali_domain_.on(gateway_id, dec_address);
|
||||
if (sent) {
|
||||
cache_.mirrorDaliCommand(gateway_id, rawCommandAddressFromDec(dec_address),
|
||||
kDaliCmdRecallMax);
|
||||
cache_.mirrorDaliCommand(gateway_id, raw_addr, kDaliCmdRecallMax);
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
@@ -1230,4 +1633,90 @@ void GatewayController::handleInternalGroupCommand(uint8_t gateway_id,
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayController::handleGatewayCacheCommand(uint8_t gateway_id,
|
||||
const std::vector<uint8_t>& command) {
|
||||
const uint8_t op = command.size() > 4 ? command[4] : kGatewayCacheOpSummary;
|
||||
const uint8_t arg = command.size() > 5 ? command[5] : 0;
|
||||
const bool enabled = config_.cache_supported && cache_.cacheEnabled();
|
||||
|
||||
if (op == kGatewayCacheOpSummary) {
|
||||
const uint8_t flags = static_cast<uint8_t>((config_.cache_supported ? 0x01 : 0x00) |
|
||||
(cache_.cacheEnabled() ? 0x02 : 0x00) |
|
||||
(cacheRefreshEnabled() ? 0x04 : 0x00));
|
||||
std::vector<uint8_t> payload{kGatewayCacheOpcode,
|
||||
gateway_id,
|
||||
op,
|
||||
kGatewayCacheStatusOk,
|
||||
kGatewayCacheProtocolVersion,
|
||||
flags};
|
||||
AppendLe16(payload, static_cast<uint16_t>(std::min<uint32_t>(
|
||||
config_.cache_refresh_interval_ms / 1000U, 0xffffU)));
|
||||
publishPayload(gateway_id, payload);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
publishPayload(gateway_id, {kGatewayCacheOpcode, gateway_id, op,
|
||||
kGatewayCacheStatusDisabled, arg});
|
||||
return;
|
||||
}
|
||||
|
||||
if (op == kGatewayCacheOpShortAddress) {
|
||||
if (arg >= kDaliShortAddressCount) {
|
||||
publishPayload(gateway_id, {kGatewayCacheOpcode, gateway_id, op,
|
||||
kGatewayCacheStatusInvalidArgument, arg});
|
||||
return;
|
||||
}
|
||||
const auto state = cache_.daliAddressState(gateway_id, arg);
|
||||
std::vector<uint8_t> payload{kGatewayCacheOpcode, gateway_id, op, kGatewayCacheStatusOk, arg};
|
||||
AppendLe16(payload, CacheAddressFlags(state));
|
||||
payload.push_back(CacheByte(state.status.actual_level));
|
||||
payload.push_back(CacheByte(state.status.scene_id));
|
||||
AppendLe16(payload, state.group_mask_known ? state.group_mask : 0);
|
||||
payload.push_back(CacheByte(state.settings.power_on_level));
|
||||
payload.push_back(CacheByte(state.settings.system_failure_level));
|
||||
payload.push_back(CacheByte(state.settings.min_level));
|
||||
payload.push_back(CacheByte(state.settings.max_level));
|
||||
payload.push_back(CacheByte(state.settings.fade_time));
|
||||
payload.push_back(CacheByte(state.settings.fade_rate));
|
||||
AppendLe32(payload, state.status.revision);
|
||||
AppendLe16(payload, CacheSceneKnownMask(state));
|
||||
for (const auto& level : state.scene_levels) {
|
||||
payload.push_back(CacheByte(level));
|
||||
}
|
||||
publishPayload(gateway_id, payload);
|
||||
return;
|
||||
}
|
||||
|
||||
if (op == kGatewayCacheOpGroup) {
|
||||
if (arg >= kDaliSceneCount) {
|
||||
publishPayload(gateway_id, {kGatewayCacheOpcode, gateway_id, op,
|
||||
kGatewayCacheStatusInvalidArgument, arg});
|
||||
return;
|
||||
}
|
||||
const auto status = cache_.daliGroupStatus(gateway_id, arg);
|
||||
std::vector<uint8_t> payload{kGatewayCacheOpcode, gateway_id, op, kGatewayCacheStatusOk, arg};
|
||||
AppendLe16(payload, CacheRuntimeFlags(status));
|
||||
payload.push_back(CacheByte(status.actual_level));
|
||||
payload.push_back(CacheByte(status.scene_id));
|
||||
AppendLe32(payload, status.revision);
|
||||
publishPayload(gateway_id, payload);
|
||||
return;
|
||||
}
|
||||
|
||||
if (op == kGatewayCacheOpBroadcast) {
|
||||
const auto status = cache_.daliBroadcastStatus(gateway_id);
|
||||
std::vector<uint8_t> payload{kGatewayCacheOpcode, gateway_id, op, kGatewayCacheStatusOk, 0};
|
||||
AppendLe16(payload, CacheRuntimeFlags(status));
|
||||
payload.push_back(CacheByte(status.actual_level));
|
||||
payload.push_back(CacheByte(status.scene_id));
|
||||
AppendLe32(payload, status.revision);
|
||||
publishPayload(gateway_id, payload);
|
||||
return;
|
||||
}
|
||||
|
||||
publishPayload(gateway_id, {kGatewayCacheOpcode, gateway_id, op,
|
||||
kGatewayCacheStatusInvalidArgument, arg});
|
||||
}
|
||||
|
||||
} // namespace gateway
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
idf_component_register(
|
||||
SRCS "src/gateway_knx.cpp"
|
||||
SRCS
|
||||
"src/gateway_knx.cpp"
|
||||
"src/gateway_knx_bridge.cpp"
|
||||
"src/gateway_knx_router_lifecycle.cpp"
|
||||
"src/gateway_knx_router_openknx.cpp"
|
||||
"src/gateway_knx_router_packets.cpp"
|
||||
"src/gateway_knx_router_services.cpp"
|
||||
"src/gateway_knx_secure_transport.cpp"
|
||||
"src/knx_device_broker.cpp"
|
||||
"src/oam_router_runtime.cpp"
|
||||
"src/ets_device_runtime.cpp"
|
||||
"src/ets_memory_loader.cpp"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES dali_cpp esp_driver_gpio esp_driver_uart esp_hw_support esp_netif freertos log lwip openknx_idf
|
||||
REQUIRES dali_cpp esp_driver_gpio esp_driver_uart esp_hw_support esp_netif freertos log lwip knx mbedtls
|
||||
)
|
||||
|
||||
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17)
|
||||
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_idf_platform.h"
|
||||
#include "ets_memory_loader.h"
|
||||
|
||||
#include "knx/bau07B0.h"
|
||||
#include "knx/cemi_frame.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class GroupObject;
|
||||
|
||||
namespace gateway::openknx {
|
||||
|
||||
class TpuartUartInterface;
|
||||
|
||||
class EtsDeviceRuntime {
|
||||
public:
|
||||
using CemiFrameSender = std::function<void(const uint8_t* data, size_t len)>;
|
||||
using CemiFrameReceiver = std::function<bool(const uint8_t* data, size_t len)>;
|
||||
using GroupWriteHandler = std::function<void(uint16_t group_address, const uint8_t* data,
|
||||
size_t len)>;
|
||||
using GroupObjectWriteHandler = std::function<void(uint16_t group_object_number,
|
||||
const uint8_t* data, size_t len)>;
|
||||
using FunctionPropertyHandler = std::function<bool(uint8_t object_index, uint8_t property_id,
|
||||
const uint8_t* data, size_t len,
|
||||
std::vector<uint8_t>* response)>;
|
||||
using FunctionPropertyExtHandler = std::function<bool(uint16_t object_type,
|
||||
uint8_t object_instance,
|
||||
uint8_t property_id,
|
||||
const uint8_t* data, size_t len,
|
||||
std::vector<uint8_t>* response)>;
|
||||
|
||||
EtsDeviceRuntime(std::string nvs_namespace,
|
||||
uint16_t fallback_individual_address,
|
||||
uint16_t tunnel_client_address = 0,
|
||||
std::unique_ptr<TpuartUartInterface> tp_uart_interface = nullptr);
|
||||
~EtsDeviceRuntime();
|
||||
|
||||
uint16_t individualAddress() const;
|
||||
uint16_t tunnelClientAddress() const;
|
||||
bool configured() const;
|
||||
bool programmingMode() const;
|
||||
void setProgrammingMode(bool enabled);
|
||||
void toggleProgrammingMode();
|
||||
EtsMemorySnapshot snapshot() const;
|
||||
uint8_t paramByte(uint32_t addr) const;
|
||||
bool paramBit(uint32_t addr, uint8_t shift) const;
|
||||
|
||||
// Accessors for OpenKNX integration (DIB construction, IP parameter object).
|
||||
DeviceObject& deviceObject();
|
||||
Platform& platform();
|
||||
|
||||
void setFunctionPropertyHandlers(FunctionPropertyHandler command_handler,
|
||||
FunctionPropertyHandler state_handler);
|
||||
void setFunctionPropertyExtHandlers(FunctionPropertyExtHandler command_handler,
|
||||
FunctionPropertyExtHandler state_handler);
|
||||
void setGroupWriteHandler(GroupWriteHandler handler);
|
||||
void setGroupObjectWriteHandler(GroupObjectWriteHandler handler);
|
||||
void setBusFrameSender(CemiFrameSender sender);
|
||||
void setTpFrameReceiver(CemiFrameReceiver receiver);
|
||||
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 handleLocalBroadcastManagementFrame(const uint8_t* data, size_t len,
|
||||
CemiFrameSender sender);
|
||||
bool handleBusFrame(const uint8_t* data, size_t len);
|
||||
bool emitGroupValue(uint16_t group_object_number, const uint8_t* data, size_t len,
|
||||
CemiFrameSender sender);
|
||||
void loop();
|
||||
|
||||
private:
|
||||
static bool HandleOutboundCemiFrame(CemiFrame& frame, void* context);
|
||||
static void EmitTunnelFrame(CemiFrame& frame, void* context);
|
||||
static void HandleSecureGroupWrite(uint16_t group_address, const uint8_t* data,
|
||||
uint8_t data_length, void* context);
|
||||
static void HandleGroupObjectWrite(GroupObject& ko);
|
||||
static bool HandleFunctionPropertyCommand(uint8_t object_index, uint8_t property_id,
|
||||
uint8_t length, uint8_t* data,
|
||||
uint8_t* result_data, uint8_t& result_length);
|
||||
static bool HandleFunctionPropertyState(uint8_t object_index, uint8_t property_id,
|
||||
uint8_t length, uint8_t* data,
|
||||
uint8_t* result_data, uint8_t& result_length);
|
||||
static bool HandleFunctionPropertyExtCommand(uint16_t object_type,
|
||||
uint8_t object_instance,
|
||||
uint8_t property_id,
|
||||
uint8_t length, uint8_t* data,
|
||||
uint8_t* result_data,
|
||||
uint8_t& result_length);
|
||||
static bool HandleFunctionPropertyExtState(uint16_t object_type,
|
||||
uint8_t object_instance,
|
||||
uint8_t property_id,
|
||||
uint8_t length, uint8_t* data,
|
||||
uint8_t* result_data,
|
||||
uint8_t& result_length);
|
||||
static uint16_t DefaultTunnelClientAddress(uint16_t individual_address);
|
||||
static bool DispatchFunctionProperty(FunctionPropertyHandler* handler, uint8_t object_index,
|
||||
uint8_t property_id, uint8_t length, uint8_t* data,
|
||||
uint8_t* result_data, uint8_t& result_length);
|
||||
static bool DispatchFunctionPropertyExt(FunctionPropertyExtHandler* handler,
|
||||
uint16_t object_type,
|
||||
uint8_t object_instance,
|
||||
uint8_t property_id,
|
||||
uint8_t length, uint8_t* data,
|
||||
uint8_t* result_data,
|
||||
uint8_t& result_length);
|
||||
void installGroupObjectCallbacks();
|
||||
bool shouldConsumeTunnelFrame(CemiFrame& frame) const;
|
||||
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_;
|
||||
CemiFrameReceiver tp_frame_receiver_;
|
||||
GroupWriteHandler group_write_handler_;
|
||||
GroupObjectWriteHandler group_object_write_handler_;
|
||||
FunctionPropertyHandler command_handler_;
|
||||
FunctionPropertyHandler state_handler_;
|
||||
FunctionPropertyExtHandler command_ext_handler_;
|
||||
FunctionPropertyExtHandler state_ext_handler_;
|
||||
bool suppress_group_object_write_callback_{false};
|
||||
uint16_t group_object_callback_count_{0};
|
||||
};
|
||||
|
||||
} // namespace gateway::openknx
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "bridge.hpp"
|
||||
#include "model_value.hpp"
|
||||
|
||||
#include "knx/ip_parameter_object.h"
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
@@ -25,11 +27,14 @@ namespace gateway {
|
||||
|
||||
namespace openknx {
|
||||
class EtsDeviceRuntime;
|
||||
class OamRouterRuntime;
|
||||
class TpuartUartInterface;
|
||||
}
|
||||
|
||||
constexpr uint16_t kGatewayKnxDefaultUdpPort = 3671;
|
||||
constexpr const char* kGatewayKnxDefaultMulticastAddress = "224.0.23.12";
|
||||
constexpr uint32_t kGatewayKnxDefaultTpBaudrate = 19200;
|
||||
constexpr uint32_t kGatewayKnxDefaultTpStartupTimeoutMs = 2000;
|
||||
|
||||
struct GatewayKnxTpUartConfig {
|
||||
int uart_port{1};
|
||||
@@ -38,6 +43,7 @@ struct GatewayKnxTpUartConfig {
|
||||
uint32_t baudrate{kGatewayKnxDefaultTpBaudrate};
|
||||
size_t rx_buffer_size{1024};
|
||||
size_t tx_buffer_size{1024};
|
||||
uint32_t startup_timeout_ms{kGatewayKnxDefaultTpStartupTimeoutMs};
|
||||
uint32_t read_timeout_ms{20};
|
||||
bool nine_bit_mode{true};
|
||||
};
|
||||
@@ -54,6 +60,40 @@ struct GatewayKnxEtsAssociation {
|
||||
uint16_t group_object_number{0};
|
||||
};
|
||||
|
||||
struct GatewayKnxCloudRemoteConfig {
|
||||
bool enabled{false};
|
||||
std::string mode{"mqtt"};
|
||||
std::string relay_endpoint;
|
||||
std::string mqtt_topic_prefix;
|
||||
std::string auth_token_ref;
|
||||
bool require_secure_tunnel{true};
|
||||
bool udp_punch_enabled{false};
|
||||
};
|
||||
|
||||
struct GatewayKnxOamRouterConfig {
|
||||
bool enabled{false};
|
||||
bool ets_database_enabled{true};
|
||||
bool secure_tunnel_enabled{true};
|
||||
bool secure_routing_enabled{true};
|
||||
uint16_t individual_address{0xff02};
|
||||
uint16_t tunnel_address_base{0xff10};
|
||||
int programming_button_gpio{-1};
|
||||
bool programming_button_active_low{true};
|
||||
int programming_led_gpio{-1};
|
||||
bool programming_led_active_high{true};
|
||||
GatewayKnxCloudRemoteConfig cloud_remote;
|
||||
};
|
||||
|
||||
struct GatewayKnxIpSecureCredentialMaterial {
|
||||
bool activated{false};
|
||||
bool backbone_key_available{false};
|
||||
std::array<uint8_t, 16> backbone_key{};
|
||||
bool device_authentication_key_available{false};
|
||||
std::array<uint8_t, 16> device_authentication_key{};
|
||||
std::vector<std::array<uint8_t, 16>> tunnel_user_keys;
|
||||
uint64_t routing_sequence{0};
|
||||
};
|
||||
|
||||
struct GatewayKnxConfig {
|
||||
bool dali_router_enabled{true};
|
||||
bool ip_router_enabled{false};
|
||||
@@ -62,6 +102,7 @@ struct GatewayKnxConfig {
|
||||
bool ets_database_enabled{true};
|
||||
GatewayKnxMappingMode mapping_mode{GatewayKnxMappingMode::kFormula};
|
||||
uint8_t main_group{0};
|
||||
uint8_t dali_bus_id{0};
|
||||
uint16_t udp_port{kGatewayKnxDefaultUdpPort};
|
||||
std::string multicast_address{kGatewayKnxDefaultMulticastAddress};
|
||||
uint16_t ip_interface_individual_address{0xff01};
|
||||
@@ -70,6 +111,7 @@ struct GatewayKnxConfig {
|
||||
bool programming_button_active_low{true};
|
||||
int programming_led_gpio{-1};
|
||||
bool programming_led_active_high{true};
|
||||
GatewayKnxOamRouterConfig oam_router;
|
||||
std::vector<GatewayKnxEtsAssociation> ets_associations;
|
||||
GatewayKnxTpUartConfig tp_uart;
|
||||
};
|
||||
@@ -80,6 +122,8 @@ enum class GatewayKnxDaliDataType : uint8_t {
|
||||
kBrightness = 2,
|
||||
kColorTemperature = 3,
|
||||
kRgb = 4,
|
||||
kBrightnessRelative = 5,
|
||||
kScene = 6,
|
||||
};
|
||||
|
||||
enum class GatewayKnxDaliTargetKind : uint8_t {
|
||||
@@ -117,8 +161,18 @@ struct GatewayKnxCommissioningBallast {
|
||||
uint8_t short_address{0xff};
|
||||
};
|
||||
|
||||
struct GatewayKnxReg1ScanOptions {
|
||||
bool only_new{false};
|
||||
bool randomize{false};
|
||||
bool delete_all{false};
|
||||
bool assign{false};
|
||||
};
|
||||
|
||||
std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value);
|
||||
DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config);
|
||||
std::optional<GatewayKnxOamRouterConfig> GatewayKnxOamRouterConfigFromValue(
|
||||
const DaliValue* value);
|
||||
DaliValue GatewayKnxOamRouterConfigToValue(const GatewayKnxOamRouterConfig& config);
|
||||
bool GatewayKnxConfigUsesTpUart(const GatewayKnxConfig& config);
|
||||
|
||||
const char* GatewayKnxMappingModeToString(GatewayKnxMappingMode mode);
|
||||
@@ -135,17 +189,19 @@ std::string GatewayKnxGroupAddressString(uint16_t group_address);
|
||||
class GatewayKnxBridge {
|
||||
public:
|
||||
explicit GatewayKnxBridge(DaliBridgeEngine& engine);
|
||||
~GatewayKnxBridge();
|
||||
|
||||
void setConfig(const GatewayKnxConfig& config);
|
||||
void setRuntimeContext(const openknx::EtsDeviceRuntime* runtime);
|
||||
const GatewayKnxConfig& config() const;
|
||||
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);
|
||||
DaliBridgeResult handleGroupObjectWrite(uint16_t group_object_number,
|
||||
const uint8_t* data, size_t len);
|
||||
bool handleFunctionPropertyCommand(uint8_t object_index, uint8_t property_id,
|
||||
const uint8_t* data, size_t len,
|
||||
std::vector<uint8_t>* response);
|
||||
@@ -158,6 +214,8 @@ class GatewayKnxBridge {
|
||||
GatewayKnxDaliDataType data_type,
|
||||
GatewayKnxDaliTarget target,
|
||||
const uint8_t* data, size_t len);
|
||||
DaliBridgeResult executeReg1SceneWrite(uint16_t group_address, const uint8_t* data,
|
||||
size_t len);
|
||||
DaliBridgeResult executeEtsBindings(uint16_t group_address,
|
||||
const std::vector<GatewayKnxDaliBinding>& bindings,
|
||||
const uint8_t* data, size_t len);
|
||||
@@ -185,10 +243,17 @@ class GatewayKnxBridge {
|
||||
std::vector<uint8_t>* response);
|
||||
bool handleReg1FoundEvgsState(const uint8_t* data, size_t len,
|
||||
std::vector<uint8_t>* response);
|
||||
static void CommissioningScanTaskEntry(void* arg);
|
||||
void runCommissioningScanTask();
|
||||
|
||||
DaliBridgeEngine& engine_;
|
||||
GatewayKnxConfig config_;
|
||||
const openknx::EtsDeviceRuntime* runtime_{nullptr};
|
||||
std::map<uint16_t, std::vector<GatewayKnxDaliBinding>> ets_bindings_by_group_address_;
|
||||
SemaphoreHandle_t commissioning_lock_{nullptr};
|
||||
TaskHandle_t commissioning_scan_task_{nullptr};
|
||||
std::atomic_bool commissioning_scan_cancel_requested_{false};
|
||||
GatewayKnxReg1ScanOptions commissioning_scan_options_;
|
||||
bool commissioning_scan_done_{true};
|
||||
bool commissioning_assign_done_{true};
|
||||
std::vector<GatewayKnxCommissioningBallast> commissioning_found_ballasts_;
|
||||
@@ -196,23 +261,42 @@ 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)>;
|
||||
using GroupObjectWriteHandler = std::function<DaliBridgeResult(uint16_t group_object_number,
|
||||
const uint8_t* data,
|
||||
size_t len)>;
|
||||
using RoutingSequenceStoreHandler = std::function<void(uint64_t sequence)>;
|
||||
using CloudCemiPublisher = std::function<void(const uint8_t* data, size_t len)>;
|
||||
|
||||
GatewayKnxTpIpRouter(GatewayKnxBridge& bridge, CemiFrameHandler handler,
|
||||
struct CloudCemiStats {
|
||||
bool enabled{false};
|
||||
uint64_t uplink_frames{0};
|
||||
uint64_t downlink_frames{0};
|
||||
};
|
||||
|
||||
GatewayKnxTpIpRouter(GatewayKnxBridge& bridge,
|
||||
std::string openknx_namespace = "openknx");
|
||||
~GatewayKnxTpIpRouter();
|
||||
|
||||
void setConfig(const GatewayKnxConfig& config);
|
||||
void setCommissioningOnly(bool enabled);
|
||||
void setGroupWriteHandler(GroupWriteHandler handler);
|
||||
void setGroupObjectWriteHandler(GroupObjectWriteHandler handler);
|
||||
void setOamIpSecureCredentials(const GatewayKnxIpSecureCredentialMaterial& credentials);
|
||||
void setOamIpSecureRoutingSequenceStoreHandler(RoutingSequenceStoreHandler handler);
|
||||
void setCloudCemiPublisher(CloudCemiPublisher publisher);
|
||||
const GatewayKnxConfig& config() const;
|
||||
bool injectCloudCemiFrame(const uint8_t* data, size_t len);
|
||||
CloudCemiStats cloudCemiStats() const;
|
||||
bool tpUartOnline() const;
|
||||
bool programmingMode();
|
||||
esp_err_t setProgrammingMode(bool enabled);
|
||||
esp_err_t toggleProgrammingMode();
|
||||
bool oamProgrammingMode();
|
||||
esp_err_t setOamProgrammingMode(bool enabled);
|
||||
esp_err_t toggleOamProgrammingMode();
|
||||
|
||||
esp_err_t start(uint32_t task_stack_size, UBaseType_t task_priority);
|
||||
esp_err_t stop();
|
||||
@@ -221,6 +305,17 @@ class GatewayKnxTpIpRouter {
|
||||
bool publishDaliStatus(const GatewayKnxDaliTarget& target, uint8_t actual_level);
|
||||
|
||||
private:
|
||||
bool handleFunctionPropertyExtCommand(uint16_t object_type,
|
||||
uint8_t object_instance,
|
||||
uint8_t property_id,
|
||||
const uint8_t* data, size_t len,
|
||||
std::vector<uint8_t>* response);
|
||||
bool handleFunctionPropertyExtState(uint16_t object_type,
|
||||
uint8_t object_instance,
|
||||
uint8_t property_id,
|
||||
const uint8_t* data, size_t len,
|
||||
std::vector<uint8_t>* response);
|
||||
|
||||
static constexpr size_t kMaxTunnelClients = 16;
|
||||
static constexpr size_t kMaxTcpClients = 4;
|
||||
|
||||
@@ -237,11 +332,34 @@ class GatewayKnxTpIpRouter {
|
||||
uint8_t connection_type{0};
|
||||
uint8_t received_sequence{255};
|
||||
uint8_t send_sequence{0};
|
||||
uint8_t last_tunnel_confirmation_sequence{0};
|
||||
uint16_t individual_address{0};
|
||||
int tcp_sock{-1};
|
||||
TickType_t last_activity_tick{0};
|
||||
::sockaddr_in control_remote{};
|
||||
::sockaddr_in data_remote{};
|
||||
std::vector<uint8_t> last_received_cemi;
|
||||
std::vector<uint8_t> last_tunnel_confirmation_packet;
|
||||
std::vector<uint8_t> last_sent_cemi;
|
||||
uint16_t secure_session_id{0};
|
||||
bool oam_router_persona{false};
|
||||
TickType_t last_sent_cemi_tick{0};
|
||||
};
|
||||
|
||||
struct SecureSession {
|
||||
bool active{false};
|
||||
bool authenticated{false};
|
||||
uint16_t session_id{0};
|
||||
int tcp_sock{-1};
|
||||
::sockaddr_in remote{};
|
||||
std::array<uint8_t, 32> client_public_key{};
|
||||
std::array<uint8_t, 32> server_public_key{};
|
||||
std::array<uint8_t, 32> shared_secret{};
|
||||
std::array<uint8_t, 16> session_key{};
|
||||
uint64_t send_sequence{0};
|
||||
uint64_t receive_sequence{0};
|
||||
uint8_t user_id{0};
|
||||
TickType_t last_activity_tick{0};
|
||||
};
|
||||
|
||||
static void TaskEntry(void* arg);
|
||||
@@ -254,25 +372,32 @@ class GatewayKnxTpIpRouter {
|
||||
void handleTcpAccept();
|
||||
void handleTcpClient(TcpClient& client);
|
||||
void closeTcpClient(TcpClient& client);
|
||||
void closeSecureSessionsForTcp(int sock);
|
||||
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(uint16_t service, const uint8_t* body, 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* body, size_t len,
|
||||
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 handleSecureSessionRequest(const uint8_t* body, size_t len,
|
||||
const ::sockaddr_in& remote);
|
||||
void handleSecureWrapper(const uint8_t* body, size_t len,
|
||||
const ::sockaddr_in& remote);
|
||||
void handleSecureGroupSync(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 sendDeviceConfigurationAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
|
||||
@@ -282,6 +407,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);
|
||||
bool 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,
|
||||
@@ -290,15 +417,18 @@ class GatewayKnxTpIpRouter {
|
||||
const ::sockaddr_in& remote, uint8_t connection_type,
|
||||
uint16_t tunnel_address);
|
||||
void sendRoutingIndication(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);
|
||||
bool sendPacket(const std::vector<uint8_t>& packet, const ::sockaddr_in& remote) const;
|
||||
bool sendPacket(const std::vector<uint8_t>& packet, const ::sockaddr_in& remote);
|
||||
bool sendPacketToTunnelClient(const TunnelClient& client,
|
||||
const std::vector<uint8_t>& packet) const;
|
||||
const std::vector<uint8_t>& packet);
|
||||
bool currentTransportAllowsTcpHpai() const;
|
||||
std::optional<std::array<uint8_t, 8>> localHpaiForRemote(const ::sockaddr_in& remote,
|
||||
bool tcp = false) const;
|
||||
|
||||
// --- OpenKNX-backed DIB construction (uses KnxIpSearchResponse / KnxIpDescriptionResponse) ---
|
||||
std::vector<uint8_t> buildOpenKnxSearchResponse(const ::sockaddr_in& remote) const;
|
||||
std::vector<uint8_t> buildOpenKnxDescriptionResponse(const ::sockaddr_in& remote) const;
|
||||
|
||||
// --- Hand-rolled DIB builders (fallback when OpenKNX is unavailable) ---
|
||||
std::vector<uint8_t> buildDeviceInfoDib(const ::sockaddr_in& remote) const;
|
||||
std::vector<uint8_t> buildSupportedServiceDib() const;
|
||||
std::vector<uint8_t> buildExtendedDeviceInfoDib() const;
|
||||
@@ -311,34 +441,63 @@ class GatewayKnxTpIpRouter {
|
||||
const ::sockaddr_in& data_remote,
|
||||
uint8_t connection_type);
|
||||
void resetTunnelClient(TunnelClient& client);
|
||||
SecureSession* findSecureSession(uint16_t session_id, const ::sockaddr_in& remote);
|
||||
const SecureSession* findSecureSession(uint16_t session_id,
|
||||
const ::sockaddr_in& remote) const;
|
||||
SecureSession* allocateSecureSession(const ::sockaddr_in& remote);
|
||||
SecureSession* activeSecureSession();
|
||||
bool wrapSecurePacket(SecureSession& session, const std::vector<uint8_t>& inner,
|
||||
std::vector<uint8_t>* wrapped);
|
||||
bool wrapSecureRoutingPacket(const std::vector<uint8_t>& inner,
|
||||
std::vector<uint8_t>* wrapped);
|
||||
bool decryptSecureWrapper(SecureSession& session, const uint8_t* body, size_t len,
|
||||
std::vector<uint8_t>* inner);
|
||||
bool decryptSecureRoutingWrapper(const uint8_t* body, size_t len,
|
||||
std::vector<uint8_t>* inner);
|
||||
bool verifySecureSessionAuth(SecureSession& session, const uint8_t* packet,
|
||||
size_t len, uint8_t* status);
|
||||
bool secureCredentialsReady() const;
|
||||
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);
|
||||
TunnelClient* response_client, uint16_t response_service,
|
||||
const uint8_t* suppress_routing_echo = nullptr,
|
||||
size_t suppress_routing_echo_len = 0);
|
||||
bool handleOamRouterTunnelFrame(const uint8_t* data, size_t len,
|
||||
TunnelClient* response_client, uint16_t response_service,
|
||||
const uint8_t* suppress_routing_echo = nullptr,
|
||||
size_t suppress_routing_echo_len = 0);
|
||||
bool handleOpenKnxBusFrame(const uint8_t* data, size_t len);
|
||||
bool transmitOpenKnxTpFrame(const uint8_t* data, size_t len);
|
||||
bool transmitOpenKnxTpFrameLocked(const uint8_t* data, size_t len);
|
||||
bool handleOpenKnxTpIngressFrame(const uint8_t* data, size_t len);
|
||||
void publishCloudCemiFrame(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;
|
||||
const char* ipInterfaceName() const;
|
||||
const char* ipInterfaceFriendlyName() const;
|
||||
void syncOpenKnxConfigFromDevice();
|
||||
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);
|
||||
void setOamProgrammingLed(bool on);
|
||||
|
||||
GatewayKnxBridge& bridge_;
|
||||
CemiFrameHandler handler_;
|
||||
GroupWriteHandler group_write_handler_;
|
||||
GroupObjectWriteHandler group_object_write_handler_;
|
||||
RoutingSequenceStoreHandler routing_sequence_store_handler_;
|
||||
CloudCemiPublisher cloud_cemi_publisher_;
|
||||
std::string openknx_namespace_;
|
||||
GatewayKnxConfig config_;
|
||||
std::unique_ptr<openknx::EtsDeviceRuntime> ets_device_;
|
||||
std::unique_ptr<openknx::OamRouterRuntime> oam_router_;
|
||||
TaskHandle_t task_handle_{nullptr};
|
||||
SemaphoreHandle_t openknx_lock_{nullptr};
|
||||
SemaphoreHandle_t startup_semaphore_{nullptr};
|
||||
@@ -355,17 +514,24 @@ class GatewayKnxTpIpRouter {
|
||||
TickType_t network_refresh_tick_{0};
|
||||
std::array<TcpClient, kMaxTcpClients> tcp_clients_{};
|
||||
std::array<TunnelClient, kMaxTunnelClients> tunnel_clients_{};
|
||||
std::array<SecureSession, kMaxTcpClients> secure_sessions_{};
|
||||
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};
|
||||
uint16_t last_secure_session_id_{0};
|
||||
uint16_t active_secure_session_id_{0};
|
||||
GatewayKnxIpSecureCredentialMaterial oam_ip_secure_credentials_{};
|
||||
bool tp_uart_online_{false};
|
||||
bool commissioning_only_{false};
|
||||
std::atomic_bool openknx_configured_{false};
|
||||
std::atomic<uint64_t> cloud_cemi_uplink_frames_{0};
|
||||
std::atomic<uint64_t> cloud_cemi_downlink_frames_{0};
|
||||
bool programming_button_last_pressed_{false};
|
||||
bool programming_led_state_{false};
|
||||
TickType_t programming_button_last_toggle_tick_{0};
|
||||
bool oam_programming_mode_{false};
|
||||
bool oam_programming_button_last_pressed_{false};
|
||||
bool oam_programming_led_state_{false};
|
||||
TickType_t oam_programming_button_last_toggle_tick_{0};
|
||||
std::string last_error_;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
// 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>
|
||||
#include <string>
|
||||
|
||||
namespace gateway {
|
||||
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
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_OEM_MANUFACTURER_ID
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_OEM_MANUFACTURER_ID 0x00FA
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_NUMBER
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_NUMBER 0xA11F
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_VERSION
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_VERSION 0x07
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GATEWAY_KNX_OAM_ROUTER_HARDWARE_ID
|
||||
#define CONFIG_GATEWAY_KNX_OAM_ROUTER_HARDWARE_ID 0x0001
|
||||
#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};
|
||||
|
||||
inline constexpr uint32_t kDaliMaxKnxInstanceCount = 16;
|
||||
inline constexpr uint32_t kReg1DaliSerialMacIncrement = 0;
|
||||
inline constexpr uint32_t kOamRouterSerialMacIncrement = kDaliMaxKnxInstanceCount;
|
||||
inline constexpr uint16_t kOamRouterDeviceDescriptor = 0x091A;
|
||||
inline constexpr uint16_t kOamRouterManufacturerId =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_OEM_MANUFACTURER_ID);
|
||||
inline constexpr uint16_t kOamRouterHardwareId =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_HARDWARE_ID);
|
||||
inline constexpr uint16_t kOamRouterApplicationNumber =
|
||||
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_NUMBER);
|
||||
inline constexpr uint8_t kOamRouterApplicationVersion =
|
||||
static_cast<uint8_t>(CONFIG_GATEWAY_KNX_OAM_ROUTER_APPLICATION_VERSION);
|
||||
inline constexpr uint8_t kOamRouterHardwareType[6] = {
|
||||
0x00,
|
||||
0x00,
|
||||
static_cast<uint8_t>((kOamRouterHardwareId >> 8) & 0xff),
|
||||
static_cast<uint8_t>(kOamRouterHardwareId & 0xff),
|
||||
kOamRouterApplicationVersion,
|
||||
0x00};
|
||||
inline constexpr uint8_t kOamRouterOrderNumber[10] = {
|
||||
'I', 'P', '-', 'R', 'o', 'u', 't', 'e', 'r', 0};
|
||||
inline constexpr uint8_t kOamRouterProgramVersion[5] = {
|
||||
static_cast<uint8_t>((kOamRouterManufacturerId >> 8) & 0xff),
|
||||
static_cast<uint8_t>(kOamRouterManufacturerId & 0xff),
|
||||
static_cast<uint8_t>((kOamRouterApplicationNumber >> 8) & 0xff),
|
||||
static_cast<uint8_t>(kOamRouterApplicationNumber & 0xff),
|
||||
kOamRouterApplicationVersion};
|
||||
|
||||
// RAII semaphore guard.
|
||||
class SemaphoreGuard {
|
||||
public:
|
||||
explicit SemaphoreGuard(SemaphoreHandle_t semaphore) : semaphore_(semaphore) {
|
||||
if (semaphore_ != nullptr) {
|
||||
xSemaphoreTake(semaphore_, portMAX_DELAY);
|
||||
locked_ = true;
|
||||
}
|
||||
}
|
||||
~SemaphoreGuard() {
|
||||
if (locked_) {
|
||||
xSemaphoreGive(semaphore_);
|
||||
}
|
||||
}
|
||||
private:
|
||||
SemaphoreHandle_t semaphore_{nullptr};
|
||||
bool locked_{false};
|
||||
};
|
||||
|
||||
// Resolve a UART IO pin from config or SoC defaults.
|
||||
inline bool ResolveUartIoPin(uart_port_t uart_port, int configured_pin,
|
||||
uint32_t pin_index, int* resolved_pin) {
|
||||
if (resolved_pin == nullptr) return false;
|
||||
if (configured_pin >= 0) {
|
||||
*resolved_pin = configured_pin;
|
||||
return true;
|
||||
}
|
||||
if (uart_port < 0 || uart_port >= SOC_UART_NUM ||
|
||||
pin_index >= SOC_UART_PINS_COUNT) {
|
||||
*resolved_pin = UART_PIN_NO_CHANGE;
|
||||
return false;
|
||||
}
|
||||
const int default_pin =
|
||||
uart_periph_signal[uart_port].pins[pin_index].default_gpio;
|
||||
if (default_pin < 0) {
|
||||
*resolved_pin = UART_PIN_NO_CHANGE;
|
||||
return false;
|
||||
}
|
||||
*resolved_pin = default_pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace knx_internal
|
||||
} // namespace gateway
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace gateway {
|
||||
|
||||
enum class KnxPortKind : uint8_t {
|
||||
kIpTunnel,
|
||||
kIpRouting,
|
||||
kTpUart,
|
||||
kCloud,
|
||||
kRfReserved,
|
||||
};
|
||||
|
||||
struct KnxIngressContext {
|
||||
KnxPortKind port{KnxPortKind::kIpTunnel};
|
||||
bool oam_persona_hint{false};
|
||||
bool broadcast_management{false};
|
||||
};
|
||||
|
||||
class KnxResponseDeduplicator {
|
||||
public:
|
||||
KnxResponseDeduplicator() = default;
|
||||
KnxResponseDeduplicator(const uint8_t* original, size_t len);
|
||||
|
||||
bool remember(const uint8_t* data, size_t len);
|
||||
bool remember(const std::vector<uint8_t>& data);
|
||||
size_t suppressedCount() const;
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> original_;
|
||||
std::vector<std::vector<uint8_t>> sent_;
|
||||
size_t suppressed_count_{0};
|
||||
};
|
||||
|
||||
} // namespace gateway
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_idf_platform.h"
|
||||
#include "ets_memory_loader.h"
|
||||
|
||||
#include "esp_netif.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "knx/cemi_frame.h"
|
||||
#include "knx/device_object.h"
|
||||
#include "knx/platform.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(ENABLE_BAU091A_PERSONA)
|
||||
#include "knx/bau091A.h"
|
||||
#endif
|
||||
|
||||
namespace gateway::openknx {
|
||||
|
||||
class OamRouterRuntime {
|
||||
public:
|
||||
using CemiFrameSender = std::function<void(const uint8_t* data, size_t len)>;
|
||||
|
||||
OamRouterRuntime(std::string nvs_namespace,
|
||||
uint16_t fallback_individual_address,
|
||||
uint16_t tunnel_client_address = 0);
|
||||
~OamRouterRuntime();
|
||||
|
||||
bool available() const;
|
||||
uint16_t individualAddress() const;
|
||||
uint16_t tunnelClientAddress() const;
|
||||
bool configured() const;
|
||||
bool programmingMode() const;
|
||||
void setProgrammingMode(bool enabled);
|
||||
void toggleProgrammingMode();
|
||||
EtsMemorySnapshot snapshot() const;
|
||||
|
||||
DeviceObject* deviceObject();
|
||||
Platform* platform();
|
||||
void setNetworkInterface(esp_netif_t* netif);
|
||||
void setBusFrameSender(CemiFrameSender sender);
|
||||
bool handleTunnelFrame(const uint8_t* data, size_t len, CemiFrameSender sender);
|
||||
bool handleLocalBroadcastManagementFrame(const uint8_t* data, size_t len,
|
||||
CemiFrameSender sender);
|
||||
bool handleBusFrame(const uint8_t* data, size_t len, CemiFrameSender sender);
|
||||
void loop();
|
||||
|
||||
private:
|
||||
static bool HandleOutboundCemiFrame(CemiFrame& frame, void* context);
|
||||
static void EmitTunnelFrame(CemiFrame& frame, void* context);
|
||||
static uint16_t DefaultTunnelClientAddress(uint16_t individual_address);
|
||||
bool shouldConsumeTunnelFrame(CemiFrame& frame) const;
|
||||
bool shouldConsumeBusFrame(CemiFrame& frame) const;
|
||||
|
||||
std::string nvs_namespace_;
|
||||
CemiFrameSender sender_;
|
||||
CemiFrameSender bus_frame_sender_;
|
||||
#if defined(ENABLE_BAU091A_PERSONA)
|
||||
EspIdfPlatform platform_;
|
||||
Bau091A device_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace gateway::openknx
|
||||
@@ -0,0 +1,225 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
|
||||
<ManufacturerData>
|
||||
<Manufacturer RefId="M-00FA">
|
||||
<Languages>
|
||||
<Language Identifier="en-US">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase_EN-0"><Translation AttributeName="Text" Text="second(s)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase_EN-1"><Translation AttributeName="Text" Text="minute(s)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase_EN-2"><Translation AttributeName="Text" Text="hour(s)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase10_EN-3"><Translation AttributeName="Text" Text="1/10 second(s)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase10_EN-0"><Translation AttributeName="Text" Text="second(s)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase10_EN-1"><Translation AttributeName="Text" Text="minute(s)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase10_EN-2"><Translation AttributeName="Text" Text="hour(s)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNo_EN-0"><Translation AttributeName="Text" Text="No" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNo_EN-1"><Translation AttributeName="Text" Text="Yes" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNoInverted_EN-1"><Translation AttributeName="Text" Text="No" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNoInverted_EN-0"><Translation AttributeName="Text" Text="Yes" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt1_EN-0"><Translation AttributeName="Text" Text="OFF (0)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt1_EN-1"><Translation AttributeName="Text" Text="ON (1)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt2_EN-4"><Translation AttributeName="Text" Text="not used" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt2_EN-0"><Translation AttributeName="Text" Text="normal OFF (00)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt2_EN-1"><Translation AttributeName="Text" Text="normal ON (01)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt2_EN-2"><Translation AttributeName="Text" Text="priority OFF (10)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt2_EN-3"><Translation AttributeName="Text" Text="priority ON (11)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt2Out_EN-0"><Translation AttributeName="Text" Text="normal OFF (00)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt2Out_EN-1"><Translation AttributeName="Text" Text="normal ON (01)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt2Out_EN-2"><Translation AttributeName="Text" Text="priority OFF (10)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ValueDpt2Out_EN-3"><Translation AttributeName="Text" Text="priority ON (11)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CombinedTimeDate_EN-1"><Translation AttributeName="Text" Text="one combined communication object" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CombinedTimeDate_EN-0"><Translation AttributeName="Text" Text="two separate communication objects" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeAll_EN-0"><Translation AttributeName="Text" Text="Communication object 'daylight saving time active'" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeAll_EN-1"><Translation AttributeName="Text" Text="Combined date/time communication object (DPT 19)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeAll_EN-2"><Translation AttributeName="Text" Text="Internal calculation" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeDE_EN-0"><Translation AttributeName="Text" Text="Communication object 'daylight saving time active'" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeDE_EN-2"><Translation AttributeName="Text" Text="Internal calculation" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeWorld_EN-0"><Translation AttributeName="Text" Text="Communication object 'daylight saving time active'" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeWorld_EN-1"><Translation AttributeName="Text" Text="Combined date/time communication object (DPT 19)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeKO_EN-0"><Translation AttributeName="Text" Text="Communication object 'daylight saving time active'" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-1"><Translation AttributeName="Text" Text="Amsterdam, Berlin, Bern, Rome, Vienna (+1 hour)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-31"><Translation AttributeName="Text" Text="Custom" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-27"><Translation AttributeName="Text" Text="UTC -11 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-26"><Translation AttributeName="Text" Text="UTC -10 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-25"><Translation AttributeName="Text" Text="UTC -9 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-24"><Translation AttributeName="Text" Text="UTC -8 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-23"><Translation AttributeName="Text" Text="UTC -7 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-22"><Translation AttributeName="Text" Text="UTC -6 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-21"><Translation AttributeName="Text" Text="UTC -5 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-20"><Translation AttributeName="Text" Text="UTC -4 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-19"><Translation AttributeName="Text" Text="UTC -3 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-18"><Translation AttributeName="Text" Text="UTC -2 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-17"><Translation AttributeName="Text" Text="UTC -1 hour" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-0"><Translation AttributeName="Text" Text="UTC +0 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-28"><Translation AttributeName="Text" Text="UTC +1 hour" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-2"><Translation AttributeName="Text" Text="UTC +2 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-3"><Translation AttributeName="Text" Text="UTC +3 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-4"><Translation AttributeName="Text" Text="UTC +4 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-5"><Translation AttributeName="Text" Text="UTC +5 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-6"><Translation AttributeName="Text" Text="UTC +6 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-7"><Translation AttributeName="Text" Text="UTC +7 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-8"><Translation AttributeName="Text" Text="UTC +8 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-9"><Translation AttributeName="Text" Text="UTC +9 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-10"><Translation AttributeName="Text" Text="UTC +10 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-11"><Translation AttributeName="Text" Text="UTC +11 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-12"><Translation AttributeName="Text" Text="UTC +12 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PeriodicSave_EN-0"><Translation AttributeName="Text" Text="Disabled" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PeriodicSave_EN-1"><Translation AttributeName="Text" Text="Every hour" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PeriodicSave_EN-2"><Translation AttributeName="Text" Text="Every 2 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PeriodicSave_EN-4"><Translation AttributeName="Text" Text="Every 4 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PeriodicSave_EN-6"><Translation AttributeName="Text" Text="Every 6 hours" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PeriodicSave_EN-24"><Translation AttributeName="Text" Text="Daily" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PeriodicSave_EN-168"><Translation AttributeName="Text" Text="Weekly" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ManualSave_EN-0"><Translation AttributeName="Text" Text="Disabled" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ManualSave_EN-1"><Translation AttributeName="Text" Text="Active with 5 min. write protection" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ManualSave_EN-2"><Translation AttributeName="Text" Text="Active with 15 min. write protection" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ManualSave_EN-3"><Translation AttributeName="Text" Text="Active with 60 min. write protection" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-0"><Translation AttributeName="Text" Text="Disabled" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-1"><Translation AttributeName="Text" Text="Prog LED" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-2"><Translation AttributeName="Text" Text="Device status" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-3"><Translation AttributeName="Text" Text="Bus status" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-4"><Translation AttributeName="Text" Text="Time status" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-10"><Translation AttributeName="Text" Text="Network status" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CustomHostname_EN-0"><Translation AttributeName="Text" Text="Default" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CustomHostname_EN-1"><Translation AttributeName="Text" Text="Custom" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OTAUpdate_EN-0"><Translation AttributeName="Text" Text="In programming mode" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OTAUpdate_EN-1"><Translation AttributeName="Text" Text="Always active" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OTAUpdate_EN-2"><Translation AttributeName="Text" Text="Disabled" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-LanMode_EN-0"><Translation AttributeName="Text" Text="Automatic" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-LanMode_EN-3"><Translation AttributeName="Text" Text="10 MBit/s (power saving)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-LanMode_EN-1"><Translation AttributeName="Text" Text="100 MBit/s" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000002"><Translation AttributeName="Text" Text="Time base" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000003"><Translation AttributeName="Text" Text="Time" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000147"><Translation AttributeName="Text" Text="Enable watchdog" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000144"><Translation AttributeName="Text" Text="Time zone" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000156"><Translation AttributeName="Text" Text="Receive via" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000159"><Translation AttributeName="Text" Text="Determine daylight saving time by" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100011"><Translation AttributeName="Text" Text="IP address" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100012"><Translation AttributeName="Text" Text="Subnet mask" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100013"><Translation AttributeName="Text" Text="Default gateway" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100001"><Translation AttributeName="Text" Text="Customize hostname" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100032"><Translation AttributeName="Text" Text="Web server" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100052"><Translation AttributeName="Text" Text="LAN mode" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100061"><Translation AttributeName="Text" Text="Time server" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-1100001"><Translation AttributeName="Text" Text="Must be no more than 24 characters and consist only of letters, numbers, and hyphens." /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_CH-BASE"><Translation AttributeName="Text" Text="OpenKNX" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_CH-NET"><Translation AttributeName="Text" Text="Network" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_MD-11000100_B-1100000"><Translation AttributeName="Text" Text="Transfer" /></TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
<Language Identifier="zh-CN">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase_EN-0"><Translation AttributeName="Text" Text="秒" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase_EN-1"><Translation AttributeName="Text" Text="分钟" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase_EN-2"><Translation AttributeName="Text" Text="小时" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase10_EN-3"><Translation AttributeName="Text" Text="1/10 秒" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNo_EN-0"><Translation AttributeName="Text" Text="否" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNo_EN-1"><Translation AttributeName="Text" Text="是" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNoInverted_EN-1"><Translation AttributeName="Text" Text="否" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNoInverted_EN-0"><Translation AttributeName="Text" Text="是" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CombinedTimeDate_EN-1"><Translation AttributeName="Text" Text="一个组合通信对象" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CombinedTimeDate_EN-0"><Translation AttributeName="Text" Text="两个独立通信对象" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeAll_EN-0"><Translation AttributeName="Text" Text="通信对象“夏令时激活”" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeAll_EN-1"><Translation AttributeName="Text" Text="组合日期/时间通信对象 (DPT 19)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeAll_EN-2"><Translation AttributeName="Text" Text="内部计算" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeDE_EN-0"><Translation AttributeName="Text" Text="通信对象“夏令时激活”" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeDE_EN-2"><Translation AttributeName="Text" Text="内部计算" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeWorld_EN-0"><Translation AttributeName="Text" Text="通信对象“夏令时激活”" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeWorld_EN-1"><Translation AttributeName="Text" Text="组合日期/时间通信对象 (DPT 19)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeKO_EN-0"><Translation AttributeName="Text" Text="通信对象“夏令时激活”" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-1"><Translation AttributeName="Text" Text="阿姆斯特丹、柏林、伯尔尼、罗马、维也纳(+1 小时)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-31"><Translation AttributeName="Text" Text="自定义" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PeriodicSave_EN-0"><Translation AttributeName="Text" Text="已禁用" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ManualSave_EN-0"><Translation AttributeName="Text" Text="已禁用" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-0"><Translation AttributeName="Text" Text="已禁用" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-1"><Translation AttributeName="Text" Text="编程 LED" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-2"><Translation AttributeName="Text" Text="设备状态" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-3"><Translation AttributeName="Text" Text="总线状态" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-4"><Translation AttributeName="Text" Text="时间状态" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-10"><Translation AttributeName="Text" Text="网络状态" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CustomHostname_EN-0"><Translation AttributeName="Text" Text="默认" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CustomHostname_EN-1"><Translation AttributeName="Text" Text="自定义" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OTAUpdate_EN-0"><Translation AttributeName="Text" Text="编程模式下" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OTAUpdate_EN-1"><Translation AttributeName="Text" Text="始终启用" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OTAUpdate_EN-2"><Translation AttributeName="Text" Text="已禁用" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-LanMode_EN-0"><Translation AttributeName="Text" Text="自动" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-LanMode_EN-3"><Translation AttributeName="Text" Text="10 MBit/s(省电)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000002"><Translation AttributeName="Text" Text="时间基准" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000003"><Translation AttributeName="Text" Text="时间" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000147"><Translation AttributeName="Text" Text="启用看门狗" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000144"><Translation AttributeName="Text" Text="时区" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000156"><Translation AttributeName="Text" Text="接收方式" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000159"><Translation AttributeName="Text" Text="夏令时判定方式" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100011"><Translation AttributeName="Text" Text="IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100012"><Translation AttributeName="Text" Text="子网掩码" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100013"><Translation AttributeName="Text" Text="默认网关" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100001"><Translation AttributeName="Text" Text="自定义主机名" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100032"><Translation AttributeName="Text" Text="Web 服务器" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100052"><Translation AttributeName="Text" Text="LAN 模式" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100061"><Translation AttributeName="Text" Text="时间服务器" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-1100001"><Translation AttributeName="Text" Text="最多 24 个字符,且只能包含字母、数字和连字符。" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_CH-NET"><Translation AttributeName="Text" Text="网络" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_MD-11000100_B-1100000"><Translation AttributeName="Text" Text="传输" /></TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
<Language Identifier="de-DE">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-2"><Translation AttributeName="Text" Text="Gerätestatus" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100032"><Translation AttributeName="Text" Text="Webserver" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-1100001"><Translation AttributeName="Text" Text="Darf nur 24 Zeichen lang sein und muss aus Buchstaben, Zahlen und Bindestrichen bestehen." /></TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
<Language Identifier="ja-JP">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase_EN-0"><Translation AttributeName="Text" Text="秒" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase_EN-1"><Translation AttributeName="Text" Text="分" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase_EN-2"><Translation AttributeName="Text" Text="時間" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-DelayBase10_EN-3"><Translation AttributeName="Text" Text="1/10 秒" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNo_EN-0"><Translation AttributeName="Text" Text="いいえ" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNo_EN-1"><Translation AttributeName="Text" Text="はい" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNoInverted_EN-1"><Translation AttributeName="Text" Text="いいえ" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OnOffYesNoInverted_EN-0"><Translation AttributeName="Text" Text="はい" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CombinedTimeDate_EN-1"><Translation AttributeName="Text" Text="1 つの結合通信オブジェクト" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CombinedTimeDate_EN-0"><Translation AttributeName="Text" Text="2 つの個別通信オブジェクト" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeAll_EN-0"><Translation AttributeName="Text" Text="通信オブジェクト「夏時間有効」" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeAll_EN-1"><Translation AttributeName="Text" Text="結合日付/時刻通信オブジェクト (DPT 19)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SummertimeAll_EN-2"><Translation AttributeName="Text" Text="内部計算" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-1"><Translation AttributeName="Text" Text="アムステルダム、ベルリン、ベルン、ローマ、ウィーン(+1 時間)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Timezone_EN-31"><Translation AttributeName="Text" Text="カスタム" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PeriodicSave_EN-0"><Translation AttributeName="Text" Text="無効" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-ManualSave_EN-0"><Translation AttributeName="Text" Text="無効" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-0"><Translation AttributeName="Text" Text="無効" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-1"><Translation AttributeName="Text" Text="Prog LED" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-2"><Translation AttributeName="Text" Text="デバイス状態" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-3"><Translation AttributeName="Text" Text="バス状態" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-4"><Translation AttributeName="Text" Text="時刻状態" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-SLEDFunc_EN-10"><Translation AttributeName="Text" Text="ネットワーク状態" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CustomHostname_EN-0"><Translation AttributeName="Text" Text="デフォルト" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-CustomHostname_EN-1"><Translation AttributeName="Text" Text="カスタム" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OTAUpdate_EN-0"><Translation AttributeName="Text" Text="プログラミングモード時" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OTAUpdate_EN-1"><Translation AttributeName="Text" Text="常に有効" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-OTAUpdate_EN-2"><Translation AttributeName="Text" Text="無効" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-LanMode_EN-0"><Translation AttributeName="Text" Text="自動" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-LanMode_EN-3"><Translation AttributeName="Text" Text="10 MBit/s(省電力)" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000002"><Translation AttributeName="Text" Text="時間基準" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000003"><Translation AttributeName="Text" Text="時間" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000147"><Translation AttributeName="Text" Text="ウォッチドッグを有効化" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000144"><Translation AttributeName="Text" Text="タイムゾーン" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000156"><Translation AttributeName="Text" Text="受信方法" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1000159"><Translation AttributeName="Text" Text="夏時間の判定方法" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100011"><Translation AttributeName="Text" Text="IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100012"><Translation AttributeName="Text" Text="サブネットマスク" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100013"><Translation AttributeName="Text" Text="デフォルトゲートウェイ" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100001"><Translation AttributeName="Text" Text="ホスト名をカスタマイズ" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100032"><Translation AttributeName="Text" Text="Web サーバー" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100052"><Translation AttributeName="Text" Text="LAN モード" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_UP-1100061"><Translation AttributeName="Text" Text="時刻サーバー" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-1100001"><Translation AttributeName="Text" Text="24 文字以内で、文字、数字、ハイフンのみを使用してください。" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_CH-NET"><Translation AttributeName="Text" Text="ネットワーク" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_MD-11000100_B-1100000"><Translation AttributeName="Text" Text="転送" /></TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
</Languages>
|
||||
</Manufacturer>
|
||||
</ManufacturerData>
|
||||
</KNX>
|
||||
@@ -0,0 +1,448 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<KNX xmlns:op="http://github.com/OpenKNX/OpenKNXproducer" xmlns="http://knx.org/xml/project/20" CreatedBy="KNX MT" ToolVersion="5.1.255.16695">
|
||||
<ManufacturerData>
|
||||
<Manufacturer>
|
||||
<ApplicationPrograms>
|
||||
<ApplicationProgram>
|
||||
<Static>
|
||||
<ParameterTypes>
|
||||
<ParameterType Id="%AID%_PT-Bool" Name="Bool">
|
||||
<TypeRestriction Base="Value" SizeInBit="1">
|
||||
<Enumeration Text="Disabled" Value="0" Id="%AID%_PT-Bool_EN-0" />
|
||||
<Enumeration Text="Enabled" Value="1" Id="%AID%_PT-Bool_EN-1" />
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-RoutingWithFilter" Name="RoutingWithFilter">
|
||||
<TypeRestriction Base="Value" SizeInBit="2">
|
||||
<Enumeration Text="Block" Value="2" Id="%AID%_PT-RoutingWithFilter_EN-2" />
|
||||
<Enumeration Text="Route" Value="1" Id="%AID%_PT-RoutingWithFilter_EN-1" />
|
||||
<Enumeration Text="Filter" Value="3" Id="%AID%_PT-RoutingWithFilter_EN-3" />
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-GrpAcknowledge" Name="GrpAcknowledge">
|
||||
<TypeRestriction Base="Value" SizeInBit="1">
|
||||
<Enumeration Text="Always" Value="0" Id="%AID%_PT-GrpAcknowledge_EN-0" />
|
||||
<Enumeration Text="Only when routed" Value="1" Id="%AID%_PT-GrpAcknowledge_EN-1" />
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-BroadcastLock" Name="BroadcastLock">
|
||||
<TypeRestriction Base="Value" SizeInBit="1">
|
||||
<Enumeration Text="Block" Value="1" Id="%AID%_PT-BroadcastLock_EN-1" />
|
||||
<Enumeration Text="Route" Value="0" Id="%AID%_PT-BroadcastLock_EN-0" />
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-PhysAcknowledge" Name="PhysAcknowledge">
|
||||
<TypeRestriction Base="Value" SizeInBit="2">
|
||||
<Enumeration Text="Only when routed" Value="1" Id="%AID%_PT-PhysAcknowledge_EN-1" />
|
||||
<Enumeration Text="Always" Value="2" Id="%AID%_PT-PhysAcknowledge_EN-2" />
|
||||
<Enumeration Text="Always reject (NACK)" Value="3" Id="%AID%_PT-PhysAcknowledge_EN-3" />
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-TunnelResOpt" Name="TunnelResOpt">
|
||||
<TypeRestriction Base="Value" SizeInBit="2">
|
||||
<Enumeration Text="Reject tunnel connection" Value="1" Id="%AID%_PT-TunnelResOpt_EN-1" />
|
||||
<Enumeration Text="Disconnect previous tunnel connection" Value="2" Id="%AID%_PT-TunnelResOpt_EN-2" />
|
||||
<Enumeration Text="Use another free tunnel" Value="3" Id="%AID%_PT-TunnelResOpt_EN-3" />
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
</ParameterTypes>
|
||||
<Parameters>
|
||||
<Parameter Id="%AID%_P-9900002" Name="AckOfPhysTelSubMain" ParameterType="%AID%_PT-PhysAcknowledge" Text="ACK for physically addressed telegrams" Value="1">
|
||||
<Property ObjectType="6" PropertyId="53" Offset="0" BitOffset="0" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900003" Name="AckOfGrpTelSubMain" ParameterType="%AID%_PT-GrpAcknowledge" Text="ACK for group telegrams" Value="1">
|
||||
<Property ObjectType="6" PropertyId="53" Offset="0" BitOffset="2" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900004" Name="BroadcastTelSubMain" ParameterType="%AID%_PT-BroadcastLock" Text="Broadcast telegrams" Value="0">
|
||||
<Property ObjectType="6" PropertyId="53" Offset="0" BitOffset="4" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900005" Name="PhysTelSubMain" ParameterType="%AID%_PT-RoutingWithFilter" Text="Physically addressed telegrams" Value="3">
|
||||
<Property ObjectType="6" PropertyId="53" Offset="0" BitOffset="6" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900006" Name="GrpTelSubMain_14_31" ParameterType="%AID%_PT-RoutingWithFilter" Text="Group telegrams (main group 14 - 31)" Value="3">
|
||||
<Property ObjectType="6" PropertyId="55" Offset="0" BitOffset="4" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900007" Name="GrpTelSubMain_0_13" ParameterType="%AID%_PT-RoutingWithFilter" Text="Group telegrams (main group 0 - 13)" Value="3">
|
||||
<Property ObjectType="6" PropertyId="55" Offset="0" BitOffset="6" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900008" Name="RepetitionBroadcastTelMainSub" ParameterType="%AID%_PT-Bool" Text="Repetition of broadcast telegrams" Value="1">
|
||||
<Property ObjectType="6" PropertyId="52" Offset="0" BitOffset="3" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900009" Name="BroadcastTelMainSub" ParameterType="%AID%_PT-BroadcastLock" Text="Broadcast telegrams" Value="0">
|
||||
<Property ObjectType="6" PropertyId="52" Offset="0" BitOffset="4" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900010" Name="RepetitionPhysTelMainSub" ParameterType="%AID%_PT-Bool" Text="Repetition of physically addressed telegrams" Value="1">
|
||||
<Property ObjectType="6" PropertyId="52" Offset="0" BitOffset="5" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900011" Name="PhysTelMainSub" ParameterType="%AID%_PT-RoutingWithFilter" Text="Physically addressed telegrams" Value="3">
|
||||
<Property ObjectType="6" PropertyId="52" Offset="0" BitOffset="6" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900012" Name="RepetitionGrpTelMainSub" ParameterType="%AID%_PT-Bool" Text="Repetition of group telegrams" Value="1">
|
||||
<Property ObjectType="6" PropertyId="54" Offset="0" BitOffset="3" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900013" Name="GrpTelMainSub_14_31" ParameterType="%AID%_PT-RoutingWithFilter" Text="Group telegrams (main group 14 - 31)" Value="3">
|
||||
<Property ObjectType="6" PropertyId="54" Offset="0" BitOffset="4" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-9900014" Name="GrpTelMainSub_0_13" ParameterType="%AID%_PT-RoutingWithFilter" Text="Group telegrams (main group 0 - 13)" Value="3">
|
||||
<Property ObjectType="6" PropertyId="54" Offset="0" BitOffset="6" />
|
||||
</Parameter>
|
||||
</Parameters>
|
||||
<ParameterRefs>
|
||||
<ParameterRef Id="%AID%_P-9900002_R-990000201" RefId="%AID%_P-9900002" Tag="42" />
|
||||
<ParameterRef Id="%AID%_P-9900003_R-990000301" RefId="%AID%_P-9900003" Tag="27" />
|
||||
<ParameterRef Id="%AID%_P-9900004_R-990000401" RefId="%AID%_P-9900004" Tag="26" />
|
||||
<ParameterRef Id="%AID%_P-9900005_R-990000501" RefId="%AID%_P-9900005" Tag="25" />
|
||||
<ParameterRef Id="%AID%_P-9900006_R-990000601" RefId="%AID%_P-9900006" Tag="24" />
|
||||
<ParameterRef Id="%AID%_P-9900007_R-990000701" RefId="%AID%_P-9900007" Tag="23" />
|
||||
<ParameterRef Id="%AID%_P-9900008_R-990000801" RefId="%AID%_P-9900008" Tag="34" />
|
||||
<ParameterRef Id="%AID%_P-9900009_R-990000901" RefId="%AID%_P-9900009" Tag="31" />
|
||||
<ParameterRef Id="%AID%_P-9900010_R-990001001" RefId="%AID%_P-9900010" Tag="33" />
|
||||
<ParameterRef Id="%AID%_P-9900011_R-990001101" RefId="%AID%_P-9900011" Tag="30" />
|
||||
<ParameterRef Id="%AID%_P-9900012_R-990001201" RefId="%AID%_P-9900012" Tag="32" />
|
||||
<ParameterRef Id="%AID%_P-9900013_R-990001301" RefId="%AID%_P-9900013" Tag="29" />
|
||||
<ParameterRef Id="%AID%_P-9900014_R-990001401" RefId="%AID%_P-9900014" Tag="28" />
|
||||
</ParameterRefs>
|
||||
<ComObjectTable>
|
||||
</ComObjectTable>
|
||||
<ComObjectRefs>
|
||||
</ComObjectRefs>
|
||||
</Static>
|
||||
<Dynamic>
|
||||
<Channel Id="%AID%_CH-%PREFIX%-Routing" Number="%PREFIX%" Name="OAM IP Router" Text="OAM IP Router" Icon="router-network">
|
||||
<ParameterBlock Id="%AID%_PB-nnn" Name="RoutingKNXIP" Text="Routing KNX -> IP" Icon="router">
|
||||
<ParameterSeparator Id="%AID%_PS-" Text="Settings for routing telegrams from TP to IP" UIHint="Headline" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900007_R-990000701" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900006_R-990000601" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900005_R-990000501" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900004_R-990000401" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900003_R-990000301" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900002_R-990000201" IndentLevel="1" />
|
||||
<choose ParamRefId="%AID%_P-9900002_R-990000201">
|
||||
<when test="3">
|
||||
<ParameterSeparator Id="%AID%_PS-" Text="With always reject (NACK), programming is only possible over IP." />
|
||||
</when>
|
||||
</choose>
|
||||
</ParameterBlock>
|
||||
<ParameterBlock Id="%AID%_PB-nnn" Name="RoutingIPKNX" Text="Routing IP -> KNX" Icon="router">
|
||||
<ParameterSeparator Id="%AID%_PS-" Text="Settings for routing telegrams from IP to TP" UIHint="Headline" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900014_R-990001401" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900013_R-990001301" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900011_R-990001101" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900009_R-990000901" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900012_R-990001201" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900010_R-990001001" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-9900008_R-990000801" IndentLevel="1" />
|
||||
</ParameterBlock>
|
||||
<ParameterBlock Id="%AID%_PB-nnn" Name="Tunnel" Text="Tunnel connections" Icon="lan-connect">
|
||||
<ParameterSeparator Id="%AID%_PS-" Text="Settings for reserved tunnel connections" UIHint="Headline" />
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="The physical addresses of tunnel connections are configured in the ETS topology view." UIHint="Information" />
|
||||
<ParameterBlock Id="%AID%_PB-nnn" Inline="true" Layout="Grid">
|
||||
<Rows>
|
||||
<Row Id="%AID%_PB-nnn_R-1" />
|
||||
</Rows>
|
||||
<Columns>
|
||||
<Column Id="%AID%_PB-nnn_C-1" Width="2%" />
|
||||
<Column Id="%AID%_PB-nnn_C-2" Width="18%" />
|
||||
<Column Id="%AID%_PB-nnn_C-3" Width="5%" />
|
||||
<Column Id="%AID%_PB-nnn_C-4" Width="20%" />
|
||||
<Column Id="%AID%_PB-nnn_C-5" Width="50%" />
|
||||
</Columns>
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Assignment" UIHint="Headline" Cell="1,2" />
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="IP address" UIHint="Headline" Cell="1,4" />
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Behavior when tunnel is busy" UIHint="Headline" Cell="1,5" />
|
||||
</ParameterBlock>
|
||||
<op:include href="Gateway-OAM-IP-Router.templ.xml" xpath="//Dynamic/ChannelIndependentBlock/*" IsInner="true" type="template" prefix="ROUTE" />
|
||||
</ParameterBlock>
|
||||
</Channel>
|
||||
</Dynamic>
|
||||
</ApplicationProgram>
|
||||
</ApplicationPrograms>
|
||||
<Languages>
|
||||
<Language Identifier="zh-CN">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%_CH-%PREFIX%-Routing">
|
||||
<Translation AttributeName="Name" Text="OAM IP 路由器" />
|
||||
<Translation AttributeName="Text" Text="OAM IP 路由器" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Bool_EN-0">
|
||||
<Translation AttributeName="Text" Text="禁用" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Bool_EN-1">
|
||||
<Translation AttributeName="Text" Text="启用" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-RoutingWithFilter_EN-2">
|
||||
<Translation AttributeName="Text" Text="阻止" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-RoutingWithFilter_EN-1">
|
||||
<Translation AttributeName="Text" Text="路由" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-RoutingWithFilter_EN-3">
|
||||
<Translation AttributeName="Text" Text="过滤" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-GrpAcknowledge_EN-0">
|
||||
<Translation AttributeName="Text" Text="始终" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-GrpAcknowledge_EN-1">
|
||||
<Translation AttributeName="Text" Text="仅在路由时" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-BroadcastLock_EN-1">
|
||||
<Translation AttributeName="Text" Text="阻止" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-BroadcastLock_EN-0">
|
||||
<Translation AttributeName="Text" Text="路由" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PhysAcknowledge_EN-1">
|
||||
<Translation AttributeName="Text" Text="仅在路由时" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PhysAcknowledge_EN-2">
|
||||
<Translation AttributeName="Text" Text="始终" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PhysAcknowledge_EN-3">
|
||||
<Translation AttributeName="Text" Text="始终拒绝 (NACK)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-TunnelResOpt_EN-1">
|
||||
<Translation AttributeName="Text" Text="拒绝隧道连接" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-TunnelResOpt_EN-2">
|
||||
<Translation AttributeName="Text" Text="断开之前的隧道连接" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-TunnelResOpt_EN-3">
|
||||
<Translation AttributeName="Text" Text="使用另一个空闲隧道" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900002">
|
||||
<Translation AttributeName="Text" Text="物理寻址报文的 ACK" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900003">
|
||||
<Translation AttributeName="Text" Text="组报文的 ACK" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900004">
|
||||
<Translation AttributeName="Text" Text="广播报文" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900005">
|
||||
<Translation AttributeName="Text" Text="物理寻址报文" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900006">
|
||||
<Translation AttributeName="Text" Text="组报文(主组 14 - 31)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900007">
|
||||
<Translation AttributeName="Text" Text="组报文(主组 0 - 13)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900008">
|
||||
<Translation AttributeName="Text" Text="重复广播报文" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900009">
|
||||
<Translation AttributeName="Text" Text="广播报文" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900010">
|
||||
<Translation AttributeName="Text" Text="重复物理寻址报文" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900011">
|
||||
<Translation AttributeName="Text" Text="物理寻址报文" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900012">
|
||||
<Translation AttributeName="Text" Text="重复组报文" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900013">
|
||||
<Translation AttributeName="Text" Text="组报文(主组 14 - 31)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900014">
|
||||
<Translation AttributeName="Text" Text="组报文(主组 0 - 13)" />
|
||||
</TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
<Language Identifier="de-DE">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%_CH-%PREFIX%-Routing">
|
||||
<Translation AttributeName="Name" Text="OAM IP-Router" />
|
||||
<Translation AttributeName="Text" Text="OAM IP-Router" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Bool_EN-0">
|
||||
<Translation AttributeName="Text" Text="Deaktiviert" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Bool_EN-1">
|
||||
<Translation AttributeName="Text" Text="Aktiviert" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-RoutingWithFilter_EN-2">
|
||||
<Translation AttributeName="Text" Text="Sperren" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-RoutingWithFilter_EN-1">
|
||||
<Translation AttributeName="Text" Text="Weiterleiten" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-RoutingWithFilter_EN-3">
|
||||
<Translation AttributeName="Text" Text="Filtern" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-GrpAcknowledge_EN-0">
|
||||
<Translation AttributeName="Text" Text="Immer" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-GrpAcknowledge_EN-1">
|
||||
<Translation AttributeName="Text" Text="Nur wenn weitergeleitet" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-BroadcastLock_EN-1">
|
||||
<Translation AttributeName="Text" Text="Sperren" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-BroadcastLock_EN-0">
|
||||
<Translation AttributeName="Text" Text="Weiterleiten" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PhysAcknowledge_EN-1">
|
||||
<Translation AttributeName="Text" Text="Nur wenn weitergeleitet" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PhysAcknowledge_EN-2">
|
||||
<Translation AttributeName="Text" Text="Immer" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PhysAcknowledge_EN-3">
|
||||
<Translation AttributeName="Text" Text="Immer ablehnen (NACK)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-TunnelResOpt_EN-1">
|
||||
<Translation AttributeName="Text" Text="Tunnelverbindung ablehnen" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-TunnelResOpt_EN-2">
|
||||
<Translation AttributeName="Text" Text="Vorherige Tunnelverbindung trennen" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-TunnelResOpt_EN-3">
|
||||
<Translation AttributeName="Text" Text="Anderen freien Tunnel verwenden" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900002">
|
||||
<Translation AttributeName="Text" Text="ACK für physikalisch adressierte Telegramme" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900003">
|
||||
<Translation AttributeName="Text" Text="ACK für Gruppentelegramme" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900004">
|
||||
<Translation AttributeName="Text" Text="Broadcast-Telegramme" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900005">
|
||||
<Translation AttributeName="Text" Text="Physikalisch adressierte Telegramme" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900006">
|
||||
<Translation AttributeName="Text" Text="Gruppentelegramme (Hauptgruppe 14 - 31)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900007">
|
||||
<Translation AttributeName="Text" Text="Gruppentelegramme (Hauptgruppe 0 - 13)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900008">
|
||||
<Translation AttributeName="Text" Text="Wiederholung von Broadcast-Telegrammen" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900009">
|
||||
<Translation AttributeName="Text" Text="Broadcast-Telegramme" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900010">
|
||||
<Translation AttributeName="Text" Text="Wiederholung von physikalisch adressierten Telegrammen" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900011">
|
||||
<Translation AttributeName="Text" Text="Physikalisch adressierte Telegramme" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900012">
|
||||
<Translation AttributeName="Text" Text="Wiederholung von Gruppentelegrammen" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900013">
|
||||
<Translation AttributeName="Text" Text="Gruppentelegramme (Hauptgruppe 14 - 31)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900014">
|
||||
<Translation AttributeName="Text" Text="Gruppentelegramme (Hauptgruppe 0 - 13)" />
|
||||
</TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
<Language Identifier="ja-JP">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%_CH-%PREFIX%-Routing">
|
||||
<Translation AttributeName="Name" Text="OAM IP ルーター" />
|
||||
<Translation AttributeName="Text" Text="OAM IP ルーター" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Bool_EN-0">
|
||||
<Translation AttributeName="Text" Text="無効" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-Bool_EN-1">
|
||||
<Translation AttributeName="Text" Text="有効" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-RoutingWithFilter_EN-2">
|
||||
<Translation AttributeName="Text" Text="ブロック" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-RoutingWithFilter_EN-1">
|
||||
<Translation AttributeName="Text" Text="ルーティング" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-RoutingWithFilter_EN-3">
|
||||
<Translation AttributeName="Text" Text="フィルター" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-GrpAcknowledge_EN-0">
|
||||
<Translation AttributeName="Text" Text="常時" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-GrpAcknowledge_EN-1">
|
||||
<Translation AttributeName="Text" Text="ルーティング時のみ" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-BroadcastLock_EN-1">
|
||||
<Translation AttributeName="Text" Text="ブロック" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-BroadcastLock_EN-0">
|
||||
<Translation AttributeName="Text" Text="ルーティング" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PhysAcknowledge_EN-1">
|
||||
<Translation AttributeName="Text" Text="ルーティング時のみ" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PhysAcknowledge_EN-2">
|
||||
<Translation AttributeName="Text" Text="常時" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-PhysAcknowledge_EN-3">
|
||||
<Translation AttributeName="Text" Text="常時拒否 (NACK)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-TunnelResOpt_EN-1">
|
||||
<Translation AttributeName="Text" Text="トンネル接続を拒否" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-TunnelResOpt_EN-2">
|
||||
<Translation AttributeName="Text" Text="以前のトンネル接続を切断" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_PT-TunnelResOpt_EN-3">
|
||||
<Translation AttributeName="Text" Text="別の空きトンネルを使用" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900002">
|
||||
<Translation AttributeName="Text" Text="物理アドレス宛テレグラムの ACK" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900003">
|
||||
<Translation AttributeName="Text" Text="グループテレグラムの ACK" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900004">
|
||||
<Translation AttributeName="Text" Text="ブロードキャストテレグラム" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900005">
|
||||
<Translation AttributeName="Text" Text="物理アドレス宛テレグラム" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900006">
|
||||
<Translation AttributeName="Text" Text="グループテレグラム (メイングループ 14 - 31)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900007">
|
||||
<Translation AttributeName="Text" Text="グループテレグラム (メイングループ 0 - 13)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900008">
|
||||
<Translation AttributeName="Text" Text="ブロードキャストテレグラムの繰り返し" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900009">
|
||||
<Translation AttributeName="Text" Text="ブロードキャストテレグラム" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900010">
|
||||
<Translation AttributeName="Text" Text="物理アドレス宛テレグラムの繰り返し" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900011">
|
||||
<Translation AttributeName="Text" Text="物理アドレス宛テレグラム" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900012">
|
||||
<Translation AttributeName="Text" Text="グループテレグラムの繰り返し" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900013">
|
||||
<Translation AttributeName="Text" Text="グループテレグラム (メイングループ 14 - 31)" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9900014">
|
||||
<Translation AttributeName="Text" Text="グループテレグラム (メイングループ 0 - 13)" />
|
||||
</TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
</Languages>
|
||||
<Baggages>
|
||||
<Baggage TargetPath="" Name="Help_en.zip" Id="%FILE-HELP-de%">
|
||||
<FileInfo TimeInfo="%DATETIME%" />
|
||||
</Baggage>
|
||||
<Baggage TargetPath="" Name="Icons.zip" Id="%FILE-ICONS%">
|
||||
<FileInfo TimeInfo="%DATETIME%" />
|
||||
</Baggage>
|
||||
</Baggages>
|
||||
</Manufacturer>
|
||||
</ManufacturerData>
|
||||
</KNX>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
|
||||
<ManufacturerData>
|
||||
<Manufacturer RefId="M-00FA">
|
||||
<ApplicationPrograms>
|
||||
<ApplicationProgram>
|
||||
<Static>
|
||||
<Parameters>
|
||||
<Parameter Id="%AID%_P-%TT%%CC%001" Name="ResTunnel%C%" ParameterType="%AID%_PT-CheckBox" Text="Reserve tunnel %C%" Value="0">
|
||||
<Property ObjectType="11" PropertyId="201" Offset="%C-1%" BitOffset="0" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-%TT%%CC%002" Name="Tunnel%C%IP" ParameterType="%AID%_PT-HostAddress" Text="IP address for tunnel %C%" Value="0.0.0.0">
|
||||
<Property ObjectType="11" PropertyId="202" Offset="%C*4-4%" BitOffset="0" />
|
||||
</Parameter>
|
||||
<Parameter Id="%AID%_P-%TT%%CC%003" Name="OptTunnel%C%" ParameterType="%AID%_PT-TunnelResOpt" Text="Behavior when tunnel is busy" Value="1">
|
||||
<Property ObjectType="11" PropertyId="201" Offset="%C-1%" BitOffset="1" />
|
||||
</Parameter>
|
||||
</Parameters>
|
||||
<ParameterRefs>
|
||||
<ParameterRef Id="%AID%_P-%TT%%CC%001_R-%TT%%CC%00101" RefId="%AID%_P-%TT%%CC%001" />
|
||||
<ParameterRef Id="%AID%_P-%TT%%CC%002_R-%TT%%CC%00201" RefId="%AID%_P-%TT%%CC%002" />
|
||||
<ParameterRef Id="%AID%_P-%TT%%CC%003_R-%TT%%CC%00301" RefId="%AID%_P-%TT%%CC%003" />
|
||||
</ParameterRefs>
|
||||
<BusInterfaces>
|
||||
<BusInterface Id="%AID%_BI-%C%" AddressIndex="%C%" AccessType="Tunneling" Text="Tunnel %C%" />
|
||||
</BusInterfaces>
|
||||
</Static>
|
||||
<Dynamic>
|
||||
<ChannelIndependentBlock>
|
||||
<ParameterBlock Id="%AID%_PB-nnn" Inline="true" Layout="Grid">
|
||||
<Rows>
|
||||
<Row Id="%AID%_PB-nnn_R-1" />
|
||||
</Rows>
|
||||
<Columns>
|
||||
<Column Id="%AID%_PB-nnn_C-1" Width="2%" />
|
||||
<Column Id="%AID%_PB-nnn_C-2" Width="18%" />
|
||||
<Column Id="%AID%_PB-nnn_C-3" Width="5%" />
|
||||
<Column Id="%AID%_PB-nnn_C-4" Width="20%" />
|
||||
<Column Id="%AID%_PB-nnn_C-5" Width="50%" />
|
||||
</Columns>
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Tunnel %C%" Cell="1,2" />
|
||||
<ParameterRefRef RefId="%AID%_P-%TT%%CC%001_R-%TT%%CC%00101" Cell="1,3" />
|
||||
<choose ParamRefId="%AID%_P-%TT%%CC%001_R-%TT%%CC%00101">
|
||||
<when test="1">
|
||||
<ParameterRefRef RefId="%AID%_P-%TT%%CC%002_R-%TT%%CC%00201" Cell="1,4" />
|
||||
<ParameterRefRef RefId="%AID%_P-%TT%%CC%003_R-%TT%%CC%00301" Cell="1,5" />
|
||||
</when>
|
||||
</choose>
|
||||
</ParameterBlock>
|
||||
</ChannelIndependentBlock>
|
||||
</Dynamic>
|
||||
</ApplicationProgram>
|
||||
</ApplicationPrograms>
|
||||
</Manufacturer>
|
||||
</ManufacturerData>
|
||||
</KNX>
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
|
||||
|
||||
<op:config name="%IsSecureEnabled%" value="true" />
|
||||
<op:config name="%NET_IPConfigTool%" value="1" />
|
||||
<op:config name="%Tunnelcount%" value="16" />
|
||||
<op:config name="%MaxUserEntries%" value="17" />
|
||||
<op:config name="%NET_ServiceNTP%" value="1" />
|
||||
<op:config name="%SecurityProxyGroupKeyTableEntries%" value="3000" />
|
||||
|
||||
<op:config name="%BASE_Info1LedLabel%" value=" (N/A)" />
|
||||
<op:config name="%BASE_Info2LedLabel%" value=" (IP)" />
|
||||
<op:config name="%BASE_Info3LedLabel%" value=" (KNX)" />
|
||||
<op:config name="%BASE_Info1LedFunc%" value="0" />
|
||||
<op:config name="%BASE_Info2LedFunc%" value="10" />
|
||||
<op:config name="%BASE_Info3LedFunc%" value="3" />
|
||||
<op:config name="%BASE_Info1LedFunc_Access%" value="ReadWrite" />
|
||||
<op:config name="%BASE_Info2LedFunc_Access%" value="ReadWrite" />
|
||||
<op:config name="%BASE_Info3LedFunc_Access%" value="ReadWrite" />
|
||||
|
||||
<op:nowarn id="1" regex="%BASE_" />
|
||||
<op:nowarn id="3" regex="BASE_getUnsupportedEtsModules" />
|
||||
|
||||
<op:ETS OpenKnxId="0xA1"
|
||||
ApplicationNumber="31"
|
||||
ApplicationVersion="0.7"
|
||||
ReplacesVersions="0.6 0.4 0.3 0.2 0.1"
|
||||
ApplicationRevision="0"
|
||||
ProductName="Gateway OAM IP Router"
|
||||
SerialNumber="1"
|
||||
OrderNumber="GatewayOAMIPRouter"
|
||||
ApplicationName="Gateway OAM IP Router"
|
||||
IsRailMounted="true"
|
||||
BusCurrent="30"
|
||||
MaskVersion="MV-091A" />
|
||||
|
||||
<op:define prefix="BASE" ModuleType="10">
|
||||
<op:verify File="deps/OGM-Common/library.json" ModuleVersion="1.7" />
|
||||
</op:define>
|
||||
<op:define prefix="NET" ModuleType="11" share="deps/OFM-Network/src/Network.share.xml">
|
||||
<op:verify File="deps/OFM-Network/library.json" ModuleVersion="0.5" />
|
||||
</op:define>
|
||||
<op:define prefix="ROUTE" NumChannels="%Tunnelcount%" ModuleType="99" share="Gateway-OAM-IP-Router.share.xml" template="Gateway-OAM-IP-Router.templ.xml" />
|
||||
|
||||
<generate base="Gateway-SecureTemplateRouter.xml" />
|
||||
|
||||
</KNX>
|
||||
@@ -0,0 +1,348 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<KNX xmlns="http://knx.org/xml/project/20" xmlns:op="http://github.com/OpenKNX/OpenKNXproducer">
|
||||
<ManufacturerData>
|
||||
<Manufacturer RefId="M-00FA">
|
||||
<Catalog>
|
||||
<CatalogSection Id="M-00FA_CS-OpenKNX" Name="OpenKNX" Number="OpenKNX" DefaultLanguage="en-US">
|
||||
<CatalogItem Id="%CatalogItemId%" Name="OpenKNX: %CatalogName%%BuildSuffixText%" Number="1" ProductRefId="%ProductId%" Hardware2ProgramRefId="%Hardware2ProgramId%" DefaultLanguage="en-US" />
|
||||
</CatalogSection>
|
||||
</Catalog>
|
||||
<ApplicationPrograms>
|
||||
<op:version OpenKnxId="%OpenKnxId%" ApplicationNumber="%ApplicationNumber%" ApplicationVersion="%ApplicationVersion%" ReplacesVersions="%ReplacesVersions%" ApplicationRevision="%ApplicationRevision%" FirmwareRevision="%FirmwareRevision%" />
|
||||
<ApplicationProgram Id="%AID%" ProgramType="ApplicationProgram" MaskVersion="%MaskVersion%" Name="%ApplicationName%%BuildSuffix%" LoadProcedureStyle="MergedProcedure" PeiType="0" DefaultLanguage="en-US" IsSecureEnabled="%IsSecureEnabled%" MaxUserEntries="%MaxUserEntries%" MaxSecurityGroupKeyTableEntries="3000" MaxSecurityIndividualAddressEntries="1000" MaxSecurityProxyGroupKeyTableEntries="%SecurityProxyGroupKeyTableEntries%" DynamicTableManagement="false" Linkable="false" MinEtsVersion="6.0" ContextHelpFile="%FILE-HELP-de%" IconFile="%FILE-ICONS%" IPConfig="Custom" ApplicationNumber="0" ApplicationVersion="0" ReplacesVersions="0" AdditionalAddressesCount="%Tunnelcount%" MaxTunnelingUserEntries="%Tunnelcount%">
|
||||
<Static>
|
||||
<Code>
|
||||
<AbsoluteSegment Id="%AID%_AS-0100" Address="256" Size="256" />
|
||||
</Code>
|
||||
<ParameterTypes>
|
||||
<op:include href="deps/OGM-Common/src/Common.share.xml" xpath="//ParameterTypes/ParameterType" prefix="BASE" />
|
||||
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/ParameterTypes/ParameterType" prefix="%prefix%" />
|
||||
</ParameterTypes>
|
||||
<Parameters>
|
||||
<op:include href="deps/OGM-Common/src/Common.Router.share.xml" xpath="//ApplicationProgram/Static/Parameters/Parameter|//ApplicationProgram/Static/Parameters/Union" type="parameter" prefix="BASE" />
|
||||
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/Parameters/Parameter|//ApplicationProgram/Static/Parameters/Union" type="parameter" prefix="%prefix%" />
|
||||
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/Static/Parameters/Parameter|//ApplicationProgram/Static/Parameters/Union" type="template" prefix="%prefix%" />
|
||||
</Parameters>
|
||||
<ParameterRefs>
|
||||
<op:include href="deps/OGM-Common/src/Common.Router.share.xml" xpath="//ApplicationProgram/Static/ParameterRefs/ParameterRef" prefix="BASE" />
|
||||
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/ParameterRefs/ParameterRef" prefix="%prefix%" />
|
||||
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/Static/ParameterRefs/ParameterRef" type="template" prefix="%prefix%" />
|
||||
</ParameterRefs>
|
||||
<ParameterCalculations>
|
||||
<op:include href="deps/OGM-Common/src/Common.share.xml" xpath="//ApplicationProgram/Static/ParameterCalculations/ParameterCalculation" prefix="BASE" />
|
||||
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/ParameterCalculations/ParameterCalculation" prefix="%prefix%" />
|
||||
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/Static/ParameterCalculations/ParameterCalculation" type="template" prefix="%prefix%" />
|
||||
</ParameterCalculations>
|
||||
<ParameterValidations>
|
||||
<op:include href="deps/OGM-Common/src/Common.share.xml" xpath="//ApplicationProgram/Static/ParameterValidations/ParameterValidation" prefix="BASE" />
|
||||
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/ParameterValidations/ParameterValidation" prefix="%prefix%" />
|
||||
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/Static/ParameterValidations/ParameterValidation" type="template" prefix="%prefix%" />
|
||||
</ParameterValidations>
|
||||
<AddressTable MaxEntries="2047" />
|
||||
<AssociationTable MaxEntries="2047" />
|
||||
<LoadProcedures>
|
||||
<LoadProcedure MergeId="1">
|
||||
<LdCtrlCompareProp InlineData="00FA" ObjIdx="0" PropId="12">
|
||||
<OnError Cause="CompareMismatch" MessageRef="%AID%_M-1" />
|
||||
</LdCtrlCompareProp>
|
||||
<LdCtrlCompareProp InlineData="%VersionCheck%" ObjIdx="0" PropId="78">
|
||||
<OnError Cause="CompareMismatch" MessageRef="%AID%_M-2" />
|
||||
</LdCtrlCompareProp>
|
||||
</LoadProcedure>
|
||||
<LoadProcedure MergeId="4">
|
||||
<LdCtrlWriteMem Address="256" Size="256" Verify="true" />
|
||||
<LdCtrlWriteProp ObjType="11" PropId="94" Count="1" Verify="true" />
|
||||
<LdCtrlWriteProp ObjType="11" PropId="97" Count="%Tunnelcount%" Verify="true" />
|
||||
<LdCtrlWriteProp ObjType="11" PropId="201" Count="%Tunnelcount%" Verify="true" />
|
||||
<LdCtrlWriteProp ObjType="11" PropId="202" Count="%Tunnelcount%" Verify="true" />
|
||||
<LdCtrlWriteProp ObjType="6" PropId="81" Count="%SecurityProxyGroupKeyTableEntries%" Verify="true" />
|
||||
</LoadProcedure>
|
||||
</LoadProcedures>
|
||||
<Messages>
|
||||
<Message Id="%AID%_M-1" Name="VersionMismatch" Text="Manufacturer identifier is incompatible." />
|
||||
<Message Id="%AID%_M-2" Name="VersionMismatch" Text="Hardware type or firmware version is incompatible." />
|
||||
<op:include href="deps/OGM-Common/src/Common.share.xml" xpath="//ApplicationProgram/Static/Messages/*" prefix="BASE" />
|
||||
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/Messages/*" prefix="%prefix%" />
|
||||
</Messages>
|
||||
<Script>
|
||||
<op:include href="deps/OGM-Common/src/Common.share.xml" xpath="//ApplicationProgram/Static/Script/text()" prefix="BASE" />
|
||||
<op:includetemplate href="%share%" xpath="//ApplicationProgram/Static/Script/text()" prefix="%prefix%" />
|
||||
</Script>
|
||||
<BusInterfaces>
|
||||
<op:include href="Gateway-OAM-IP-Router.templ.xml" xpath="//BusInterfaces/BusInterface" type="template" prefix="ROUTE" />
|
||||
</BusInterfaces>
|
||||
<Options LineCoupler0912NewProgrammingStyle="true" MaxRoutingApduLength="66" Comparable="true" Reconstructable="true" DownloadInvisibleParameters="None" SupportsExtendedMemoryServices="true" SupportsExtendedPropertyServices="true" SupportsIpSystemBroadcast="true" />
|
||||
</Static>
|
||||
<ModuleDefs>
|
||||
<op:include href="deps/OGM-Common/src/Common.share.xml" xpath="//ApplicationProgram/ModuleDefs/*" prefix="BASE" />
|
||||
<op:includetemplate href="%share%" xpath="//ApplicationProgram/ModuleDefs/*" prefix="%prefix%" />
|
||||
<op:includetemplate href="%templ%" xpath="//ApplicationProgram/ModuleDefs/*" type="template" prefix="%prefix%" />
|
||||
</ModuleDefs>
|
||||
<Dynamic>
|
||||
<op:include href="deps/OGM-Common/src/Common.Router.share.xml" xpath="//Dynamic/*" prefix="BASE" />
|
||||
<op:include href="deps/OFM-Network/src/Network.share.xml" xpath="//Dynamic/*" prefix="NET" />
|
||||
<op:include href="Gateway-OAM-IP-Router.share.xml" xpath="//Dynamic/*" prefix="ROUTE" />
|
||||
</Dynamic>
|
||||
</ApplicationProgram>
|
||||
</ApplicationPrograms>
|
||||
<Baggages>
|
||||
<op:include href="deps/OGM-Common/src/Common.share.xml" xpath="//Baggages/*" prefix="BASE" />
|
||||
<op:includetemplate href="%share%" xpath="//Manufacturer/Baggages/*" prefix="%prefix%" />
|
||||
</Baggages>
|
||||
<Hardware>
|
||||
<Hardware Id="%HardwareId%" Name="OpenKNX-%HardwareName%%BuildSuffix%" SerialNumber="%SerialNumber%" VersionNumber="1" BusCurrent="%BusCurrent%" HasIndividualAddress="true" IsCoupler="true" HasApplicationProgram="true" IsIPEnabled="1">
|
||||
<Products>
|
||||
<Product Id="%ProductId%" Text="OpenKNX: %ProductName%%BuildSuffixText%" OrderNumber="%OrderNumber%" IsRailMounted="%IsRailMounted%" WidthInMillimeter="18" DefaultLanguage="en-US">
|
||||
<RegistrationInfo RegistrationStatus="Registered" />
|
||||
</Product>
|
||||
</Products>
|
||||
<Hardware2Programs>
|
||||
<Hardware2Program Id="%Hardware2ProgramId%" MediumTypes="%MediumTypes%" CouplerCapabilities="SecurityProxy">
|
||||
<ApplicationProgramRef RefId="%AID%" />
|
||||
<RegistrationInfo RegistrationStatus="Registered" RegistrationNumber="0001/%HardwareVersionEncoded%1" />
|
||||
</Hardware2Program>
|
||||
</Hardware2Programs>
|
||||
</Hardware>
|
||||
</Hardware>
|
||||
<Languages>
|
||||
<Language Identifier="zh-CN">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%">
|
||||
<Translation AttributeName="Name" Text="网关 OAM IP 路由器%BuildSuffix%" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-1">
|
||||
<Translation AttributeName="Text" Text="制造商标识不兼容。" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-2">
|
||||
<Translation AttributeName="Text" Text="硬件类型或固件版本不兼容。" />
|
||||
</TranslationElement>
|
||||
<op:include href="Gateway-OAM-IP-Router.share.xml" xpath="//Manufacturer/Languages/Language[@Identifier='zh-CN']/TranslationUnit/TranslationElement" prefix="ROUTE" />
|
||||
<op:include href="Gateway-Dependency-Translations.xml" xpath="//Manufacturer/Languages/Language[@Identifier='zh-CN']/TranslationUnit/TranslationElement" />
|
||||
<TranslationElement RefId="%AID%_BI-1"><Translation AttributeName="Text" Text="隧道 1" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9901001"><Translation AttributeName="Text" Text="预留隧道 1" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9901002"><Translation AttributeName="Text" Text="隧道 1 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9901003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-2"><Translation AttributeName="Text" Text="隧道 2" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9902001"><Translation AttributeName="Text" Text="预留隧道 2" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9902002"><Translation AttributeName="Text" Text="隧道 2 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9902003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-3"><Translation AttributeName="Text" Text="隧道 3" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9903001"><Translation AttributeName="Text" Text="预留隧道 3" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9903002"><Translation AttributeName="Text" Text="隧道 3 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9903003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-4"><Translation AttributeName="Text" Text="隧道 4" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9904001"><Translation AttributeName="Text" Text="预留隧道 4" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9904002"><Translation AttributeName="Text" Text="隧道 4 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9904003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-5"><Translation AttributeName="Text" Text="隧道 5" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9905001"><Translation AttributeName="Text" Text="预留隧道 5" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9905002"><Translation AttributeName="Text" Text="隧道 5 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9905003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-6"><Translation AttributeName="Text" Text="隧道 6" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9906001"><Translation AttributeName="Text" Text="预留隧道 6" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9906002"><Translation AttributeName="Text" Text="隧道 6 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9906003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-7"><Translation AttributeName="Text" Text="隧道 7" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9907001"><Translation AttributeName="Text" Text="预留隧道 7" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9907002"><Translation AttributeName="Text" Text="隧道 7 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9907003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-8"><Translation AttributeName="Text" Text="隧道 8" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9908001"><Translation AttributeName="Text" Text="预留隧道 8" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9908002"><Translation AttributeName="Text" Text="隧道 8 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9908003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-9"><Translation AttributeName="Text" Text="隧道 9" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9909001"><Translation AttributeName="Text" Text="预留隧道 9" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9909002"><Translation AttributeName="Text" Text="隧道 9 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9909003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-10"><Translation AttributeName="Text" Text="隧道 10" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9910001"><Translation AttributeName="Text" Text="预留隧道 10" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9910002"><Translation AttributeName="Text" Text="隧道 10 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9910003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-11"><Translation AttributeName="Text" Text="隧道 11" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9911001"><Translation AttributeName="Text" Text="预留隧道 11" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9911002"><Translation AttributeName="Text" Text="隧道 11 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9911003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-12"><Translation AttributeName="Text" Text="隧道 12" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9912001"><Translation AttributeName="Text" Text="预留隧道 12" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9912002"><Translation AttributeName="Text" Text="隧道 12 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9912003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-13"><Translation AttributeName="Text" Text="隧道 13" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9913001"><Translation AttributeName="Text" Text="预留隧道 13" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9913002"><Translation AttributeName="Text" Text="隧道 13 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9913003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-14"><Translation AttributeName="Text" Text="隧道 14" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9914001"><Translation AttributeName="Text" Text="预留隧道 14" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9914002"><Translation AttributeName="Text" Text="隧道 14 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9914003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-15"><Translation AttributeName="Text" Text="隧道 15" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9915001"><Translation AttributeName="Text" Text="预留隧道 15" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9915002"><Translation AttributeName="Text" Text="隧道 15 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9915003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-16"><Translation AttributeName="Text" Text="隧道 16" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9916001"><Translation AttributeName="Text" Text="预留隧道 16" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9916002"><Translation AttributeName="Text" Text="隧道 16 的 IP 地址" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9916003"><Translation AttributeName="Text" Text="隧道忙碌时的行为" /></TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
<Language Identifier="de-DE">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%">
|
||||
<Translation AttributeName="Name" Text="Gateway OAM IP-Router%BuildSuffix%" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-1">
|
||||
<Translation AttributeName="Text" Text="Herstellerkennung ist inkompatibel." />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-2">
|
||||
<Translation AttributeName="Text" Text="Hardwaretyp oder Firmwareversion ist inkompatibel." />
|
||||
</TranslationElement>
|
||||
<op:include href="Gateway-OAM-IP-Router.share.xml" xpath="//Manufacturer/Languages/Language[@Identifier='de-DE']/TranslationUnit/TranslationElement" prefix="ROUTE" />
|
||||
<op:include href="Gateway-Dependency-Translations.xml" xpath="//Manufacturer/Languages/Language[@Identifier='de-DE']/TranslationUnit/TranslationElement" />
|
||||
<TranslationElement RefId="%AID%_BI-1"><Translation AttributeName="Text" Text="Tunnel 1" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9901001"><Translation AttributeName="Text" Text="Tunnel 1 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9901002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 1" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9901003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-2"><Translation AttributeName="Text" Text="Tunnel 2" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9902001"><Translation AttributeName="Text" Text="Tunnel 2 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9902002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 2" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9902003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-3"><Translation AttributeName="Text" Text="Tunnel 3" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9903001"><Translation AttributeName="Text" Text="Tunnel 3 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9903002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 3" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9903003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-4"><Translation AttributeName="Text" Text="Tunnel 4" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9904001"><Translation AttributeName="Text" Text="Tunnel 4 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9904002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 4" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9904003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-5"><Translation AttributeName="Text" Text="Tunnel 5" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9905001"><Translation AttributeName="Text" Text="Tunnel 5 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9905002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 5" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9905003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-6"><Translation AttributeName="Text" Text="Tunnel 6" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9906001"><Translation AttributeName="Text" Text="Tunnel 6 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9906002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 6" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9906003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-7"><Translation AttributeName="Text" Text="Tunnel 7" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9907001"><Translation AttributeName="Text" Text="Tunnel 7 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9907002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 7" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9907003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-8"><Translation AttributeName="Text" Text="Tunnel 8" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9908001"><Translation AttributeName="Text" Text="Tunnel 8 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9908002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 8" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9908003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-9"><Translation AttributeName="Text" Text="Tunnel 9" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9909001"><Translation AttributeName="Text" Text="Tunnel 9 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9909002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 9" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9909003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-10"><Translation AttributeName="Text" Text="Tunnel 10" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9910001"><Translation AttributeName="Text" Text="Tunnel 10 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9910002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 10" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9910003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-11"><Translation AttributeName="Text" Text="Tunnel 11" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9911001"><Translation AttributeName="Text" Text="Tunnel 11 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9911002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 11" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9911003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-12"><Translation AttributeName="Text" Text="Tunnel 12" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9912001"><Translation AttributeName="Text" Text="Tunnel 12 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9912002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 12" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9912003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-13"><Translation AttributeName="Text" Text="Tunnel 13" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9913001"><Translation AttributeName="Text" Text="Tunnel 13 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9913002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 13" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9913003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-14"><Translation AttributeName="Text" Text="Tunnel 14" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9914001"><Translation AttributeName="Text" Text="Tunnel 14 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9914002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 14" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9914003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-15"><Translation AttributeName="Text" Text="Tunnel 15" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9915001"><Translation AttributeName="Text" Text="Tunnel 15 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9915002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 15" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9915003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-16"><Translation AttributeName="Text" Text="Tunnel 16" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9916001"><Translation AttributeName="Text" Text="Tunnel 16 reservieren" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9916002"><Translation AttributeName="Text" Text="IP-Adresse für Tunnel 16" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9916003"><Translation AttributeName="Text" Text="Verhalten bei belegtem Tunnel" /></TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
<Language Identifier="ja-JP">
|
||||
<TranslationUnit RefId="%AID%">
|
||||
<TranslationElement RefId="%AID%">
|
||||
<Translation AttributeName="Name" Text="ゲートウェイ OAM IP ルーター%BuildSuffix%" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-1">
|
||||
<Translation AttributeName="Text" Text="メーカー識別子に互換性がありません。" />
|
||||
</TranslationElement>
|
||||
<TranslationElement RefId="%AID%_M-2">
|
||||
<Translation AttributeName="Text" Text="ハードウェアタイプまたはファームウェアバージョンに互換性がありません。" />
|
||||
</TranslationElement>
|
||||
<op:include href="Gateway-OAM-IP-Router.share.xml" xpath="//Manufacturer/Languages/Language[@Identifier='ja-JP']/TranslationUnit/TranslationElement" prefix="ROUTE" />
|
||||
<op:include href="Gateway-Dependency-Translations.xml" xpath="//Manufacturer/Languages/Language[@Identifier='ja-JP']/TranslationUnit/TranslationElement" />
|
||||
<TranslationElement RefId="%AID%_BI-1"><Translation AttributeName="Text" Text="トンネル 1" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9901001"><Translation AttributeName="Text" Text="トンネル 1 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9901002"><Translation AttributeName="Text" Text="トンネル 1 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9901003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-2"><Translation AttributeName="Text" Text="トンネル 2" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9902001"><Translation AttributeName="Text" Text="トンネル 2 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9902002"><Translation AttributeName="Text" Text="トンネル 2 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9902003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-3"><Translation AttributeName="Text" Text="トンネル 3" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9903001"><Translation AttributeName="Text" Text="トンネル 3 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9903002"><Translation AttributeName="Text" Text="トンネル 3 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9903003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-4"><Translation AttributeName="Text" Text="トンネル 4" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9904001"><Translation AttributeName="Text" Text="トンネル 4 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9904002"><Translation AttributeName="Text" Text="トンネル 4 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9904003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-5"><Translation AttributeName="Text" Text="トンネル 5" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9905001"><Translation AttributeName="Text" Text="トンネル 5 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9905002"><Translation AttributeName="Text" Text="トンネル 5 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9905003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-6"><Translation AttributeName="Text" Text="トンネル 6" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9906001"><Translation AttributeName="Text" Text="トンネル 6 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9906002"><Translation AttributeName="Text" Text="トンネル 6 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9906003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-7"><Translation AttributeName="Text" Text="トンネル 7" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9907001"><Translation AttributeName="Text" Text="トンネル 7 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9907002"><Translation AttributeName="Text" Text="トンネル 7 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9907003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-8"><Translation AttributeName="Text" Text="トンネル 8" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9908001"><Translation AttributeName="Text" Text="トンネル 8 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9908002"><Translation AttributeName="Text" Text="トンネル 8 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9908003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-9"><Translation AttributeName="Text" Text="トンネル 9" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9909001"><Translation AttributeName="Text" Text="トンネル 9 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9909002"><Translation AttributeName="Text" Text="トンネル 9 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9909003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-10"><Translation AttributeName="Text" Text="トンネル 10" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9910001"><Translation AttributeName="Text" Text="トンネル 10 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9910002"><Translation AttributeName="Text" Text="トンネル 10 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9910003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-11"><Translation AttributeName="Text" Text="トンネル 11" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9911001"><Translation AttributeName="Text" Text="トンネル 11 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9911002"><Translation AttributeName="Text" Text="トンネル 11 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9911003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-12"><Translation AttributeName="Text" Text="トンネル 12" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9912001"><Translation AttributeName="Text" Text="トンネル 12 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9912002"><Translation AttributeName="Text" Text="トンネル 12 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9912003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-13"><Translation AttributeName="Text" Text="トンネル 13" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9913001"><Translation AttributeName="Text" Text="トンネル 13 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9913002"><Translation AttributeName="Text" Text="トンネル 13 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9913003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-14"><Translation AttributeName="Text" Text="トンネル 14" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9914001"><Translation AttributeName="Text" Text="トンネル 14 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9914002"><Translation AttributeName="Text" Text="トンネル 14 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9914003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-15"><Translation AttributeName="Text" Text="トンネル 15" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9915001"><Translation AttributeName="Text" Text="トンネル 15 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9915002"><Translation AttributeName="Text" Text="トンネル 15 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9915003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_BI-16"><Translation AttributeName="Text" Text="トンネル 16" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9916001"><Translation AttributeName="Text" Text="トンネル 16 を予約" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9916002"><Translation AttributeName="Text" Text="トンネル 16 の IP アドレス" /></TranslationElement>
|
||||
<TranslationElement RefId="%AID%_P-9916003"><Translation AttributeName="Text" Text="トンネル使用中の動作" /></TranslationElement>
|
||||
</TranslationUnit>
|
||||
</Language>
|
||||
</Languages>
|
||||
</Manufacturer>
|
||||
</ManufacturerData>
|
||||
</KNX>
|
||||
@@ -0,0 +1,55 @@
|
||||
# Gateway OAM IP Router KNX Product XML
|
||||
|
||||
This folder contains the gateway-owned OpenKNXproducer source for the OAM-compatible BAU091A KNX/IP Router persona.
|
||||
|
||||
The source is derived from the upstream OpenKNX OAM-IP-Router database and merges the secure application metadata used by OAM-AccessControl:
|
||||
|
||||
- Manufacturer: `0x00FA`
|
||||
- Application: `0xA11F`
|
||||
- Version: `0x07`
|
||||
- Mask: `MV-091A`
|
||||
- Tunnels: `16`
|
||||
- Security: `IsSecureEnabled="true"`
|
||||
- Default language: `en-US`
|
||||
- Translations: `zh-CN`, `de-DE`, `ja-JP`
|
||||
|
||||
The XML keeps the upstream router/coupler shape and absolute memory segment while adding KNXnet/IP Secure download hooks for:
|
||||
|
||||
- OT-11 PID 94, `PID_SECURED_SERVICE_FAMILIES`
|
||||
- OT-11 PID 97, `PID_TUNNELLING_USERS`
|
||||
- OT-6 PID 81, `PID_SECURITY_PROXY_GRP_KEY_TABLE`
|
||||
|
||||
The secure proxy capability is advertised with `CouplerCapabilities="SecurityProxy"` on `Hardware2Program` and `MaxSecurityProxyGroupKeyTableEntries` on the application program, matching the secure proxy-enabled ETS database shape.
|
||||
|
||||
The OpenKNX module dependencies needed by this producer source are vendored under `deps/` so generation does not require an external OpenKNX checkout:
|
||||
|
||||
- `deps/OGM-Common/` provides `Common.share.xml`, `Common.Router.share.xml`, `InfoLed.part.xml`, `Common.script.js`, `library.json`, and producer baggage assets.
|
||||
- `deps/OFM-Network/` provides `Network.share.xml`, `Network.script.js`, `library.json`, and producer baggage assets.
|
||||
|
||||
Dependency strings are localized by `Gateway-Dependency-Translations.xml`. Dependency context-help documents are kept with the vendored modules in German plus translated English, Chinese, and Japanese help folders; the generated application uses the English help source as its default context help through OpenKNXproducer's legacy context-help baggage placeholder. OpenKNXproducer still emits that selected context-help baggage with its legacy `Help_de.zip` generated name, so keep the `Help_en.zip` source mapped to the `%FILE-HELP-de%` placeholder unless producer support for language-specific context-help baggage changes.
|
||||
|
||||
The runtime identity must stay synchronized with `gateway/components/gateway_knx/include/gateway_knx_internal.h` and the OAM router Kconfig defaults in `gateway/apps/gateway/main/Kconfig.projbuild`.
|
||||
|
||||
## Validation
|
||||
|
||||
Run a well-formed XML check from the repository root:
|
||||
|
||||
```sh
|
||||
xmllint --noout \
|
||||
gateway/components/gateway_knx/oam_knxprod/*.xml \
|
||||
gateway/components/gateway_knx/oam_knxprod/deps/OGM-Common/src/*.xml \
|
||||
gateway/components/gateway_knx/oam_knxprod/deps/OFM-Network/src/*.xml
|
||||
```
|
||||
|
||||
Then run OpenKNXproducer with `Gateway-OAM-IP-Router.xml` and import the generated product database into ETS. ETS validation should confirm the router imports as a secure-capable KNX/IP router, exposes 16 tunneling interfaces, and supports secure tunneling plus security proxy table download/verify.
|
||||
|
||||
For a local sanity check without ETS or the KNX XSD support folder installed, run:
|
||||
|
||||
```sh
|
||||
OpenKNXproducer create --NoXsd \
|
||||
-h /tmp/gateway_oam_knxprod.h \
|
||||
-o /tmp/Gateway-OAM-IP-Router.knxprod \
|
||||
gateway/components/gateway_knx/oam_knxprod/Gateway-OAM-IP-Router.xml
|
||||
```
|
||||
|
||||
This verifies OpenKNXproducer include expansion, generated IDs, translation references, and internal sanity checks. A full `.knxprod` export still requires ETS on the machine running OpenKNXproducer.
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "OFM-Network",
|
||||
"version": "0.5.4",
|
||||
"dependencies": {
|
||||
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
### Allgemein
|
||||
|
||||
In diesem Abschnitt werden die Basiseinstellungen und verfügbaren Dienste festgelegt.
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
### Dokumentation
|
||||
|
||||
|
||||
Eine vollständige Applikationsbeschreibung ist unter folgendem Link verfügbar: https://github.com/openknx/OFM-Network/blob/v1/doc/Applikationsbeschreibung-Netzwerk.md
|
||||
|
||||
Über die Netzwerkeinstellungen kann nicht nur die IP-Adresse des Geräts angepasst werden, sondern es können auch verschiedene Dienste ein- oder ausgeschaltet werden. Dazu gehören z. B. der NTP-Client zum Abrufen der aktuellen Zeit, mDNS für das automatisierte Auffinden von OpenKNX-Geräten sowie die Möglichkeit, die Geräte-Firmware per Netzwerk zu aktualisieren.
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
### Erweitere Einstellungen
|
||||
|
||||
In diesem Abschnitt werden Einstellungen vorgenommen die vorwiegen von Netzwerkexperten benötigt werden.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
### Webserver
|
||||
|
||||
Hier kann später ein Webserver aktiviert werden, der dann über den Browser aufgerufen werden kann. Diese Funktion ist derzeit noch nicht integriert und dient aktuell nur als Platzhalter.
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
### Hostname
|
||||
|
||||
Der Hostname wird automatisch aus der Seriennummer generiert (OpenKNX-XXXXXXXX) und erfordert in der Regel keine Anpassung. Sollte jedoch eine individuelle Anpassung gewünscht sein, darf die Länge von 24 Zeichen nicht überschritten werden. Darüber hinaus sind nur Buchstaben, Zahlen und Bindestriche erlaubt. Der Hostname muss zudem mit einem Buchstaben beginnen und darf nicht mit einem Bindestrich enden.
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
### IP-Adresse
|
||||
|
||||
In diesem Eingabeformular kannst du entscheiden, ob die IP-Adresse dynamisch durch einen DHCP-Server zugewiesen oder manuell festgelegt werden soll. Bei manueller Konfiguration sind neben der IP-Adresse auch die Netzmaske, ein Standardgateway (Router) und ein Nameserver (DNS) erforderlich.
|
||||
|
||||
**Hinweis**: Es wird empfohlen, die DHCP-Einstellungen beizubehalten und stattdessen eine feste IP-Adresse direkt im Router zuzuweisen. Dies liegt daran, dass die Netzwerkeinstellungen nur im programmierten Zustand gelten. Nach einem Update kann das Gerät beispielsweise wieder in den DHCP-Modus wechseln, bis es erneut programmiert wird.
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
### LAN-Modus
|
||||
|
||||
|
||||
Wähle den gewünschten Modus für die LAN-Schnittstelle aus. Die Auswahl des 10 MBit/s Modus kann genutzt werden, um den Stromverbrauch zu reduzieren.
|
||||
|
||||
**Hinweis**: In neueren Switches mit Geschwindigkeiten ab 2,5 GBit/s ist der 10 MBit/s Modus in der Regel nicht mehr vorgesehen. Es besteht daher die Möglichkeit, dass in solchen Fällen keine Verbindung hergestellt werden kann. Dennoch lohnt es sich, dies auszuprobieren, da einige Geräte diesen Modus unterstützen.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
### mDNS
|
||||
|
||||
Der mDNS Service ermöglicht das Auflösen von "Hostname.local" und kann auch später zum Auffinden von OpenKNX-Geräten im eigenen Netzwerk genutzt werden.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
### NTP-Client
|
||||
|
||||
Durch das Aktivieren des NTP-Clients kann das Gerät die aktuelle Zeit zyklisch von einem Zeitserver abrufen, anstatt sie vom Bus zu beziehen. Zudem kann das Gerät auf Wunsch die aktuelle Zeit auch auf den Bus senden.
|
||||
Die bisherigen Einstellungen bzw. Kommunikationsobjekte zum Abrufen der Zeit vom Bus entfallen. Stattdessen stehen drei neue Kommunikationsobjekte zur Verfügung, mit denen Zeit, Datum und beides kombiniert auf dem Bus bereitgestellt werden können.
|
||||
|
||||
Außerdem kann der Zeitserver (NTP-Server) angepasst werden, von dem die aktuelle Zeit bezogen wird. In der Regel ist eine Änderung nicht erforderlich, da der voreingestellte Server (pool.ntp.org) zuverlässig arbeitet und weit verbreitet ist. Dieser Server fungiert als Alias für eine Vielzahl von Zeitservern.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
### OTA-Update
|
||||
|
||||
Ermöglicht eine direkte Firmwareaktualisierung, ohne den Einsatz von KNX oder einem USB-Anschluss.
|
||||
|
||||
* **Im Prog-Modus:** Für ein Update muss das Gerät zuvor in den Programmiermodus versetzt werden (z. B. durch Drücken der PROG-Taste).
|
||||
* **Immer aktiv:** Aktiviert den dauerhaften Update-Modus für das Gerät. Wir raten von diesem Modus ab, da es schnell zu Verwechslungen zwischen Geräten kommen kann.
|
||||
* **Ausgeschaltet** Deaktiviert die Möglichkeit, Updates über das Netzwerk durchzuführen.
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
### Webserver
|
||||
|
||||
Hier kann später der Webserver konfiguriert werden, der dann über den Browser aufgerufen werden kann. Diese Funktion ist derzeit noch nicht integriert und dient aktuell nur als Platzhalter.
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
### WiFi-Assistent
|
||||
|
||||
Dieser WiFi-Assistent ermöglicht das Übertragen von WiFi-Zugangsdaten auf das Gerät. Voraussetzung ist, dass die verwendete Hardware einen WiFi-Adapter verwendet. Geräte, die per IP-Netzwerk angebunden werden, müssen bereits über eine WiFi-Verbindung und somit über gültige Zugangsdaten verfügen. Der Assistent kann daher nur die bestehenden Zugangsdaten ändern. TP-Geräte können hingegen immer per Bus angepasst werden.
|
||||
|
||||
IP-Geräte ohne WiFi-Zugangsdaten müssen initial auf anderem Wege eingerichtet werden. Dies hängt sowohl von der Gerätesoftware als auch der verwendeten Hardware ab. Die Einrichtung per Terminal (USB) sollte immer funktionieren. Dafür muss auf der Konsole nur `wifi SSID PSK` eingegeben werden.
|
||||
|
||||
Bei Geräten auf Arduino-Pico-Basis können die WiFi-Zugangsdaten in der Regel zusätzlich per USB übertragen werden. Dazu muss das Gerät am Rechner angeschlossen und der Transfermodus durch einen Doppelklick auf die Prog-Taste gestartet werden. Kopiere dann eine Datei namens "WIFI.TXT" auf das Wechsellaufwerk. Die erste Zeile muss die SSID und die zweite den PSK enthalten. Danach den Modus wieder mit einem Doppelklick beenden und das Gerät neu starten.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
### Documentation
|
||||
|
||||
A complete application description is available at: https://github.com/openknx/OFM-Network/blob/v1/doc/Applikationsbeschreibung-Netzwerk.md
|
||||
|
||||
The network settings can be used not only to adjust the device IP address, but also to enable or disable several services. These include the NTP client for retrieving the current time, mDNS for automated discovery of OpenKNX devices, and the option to update the device firmware over the network.
|
||||
@@ -0,0 +1,3 @@
|
||||
### Web Server
|
||||
|
||||
A web server can be enabled here in the future and then opened in a browser. This function is not currently integrated and is only a placeholder.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### Hostname
|
||||
|
||||
The hostname is generated automatically from the serial number (OpenKNX-XXXXXXXX) and normally does not need to be changed. If a custom hostname is desired, it must not exceed 24 characters. Only letters, numbers, and hyphens are allowed. The hostname must start with a letter and must not end with a hyphen.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
### IP Address
|
||||
|
||||
In this form you can decide whether the IP address is assigned dynamically by a DHCP server or set manually. For manual configuration, the subnet mask, a default gateway (router), and a name server (DNS) are required in addition to the IP address.
|
||||
|
||||
**Note:** It is recommended to keep DHCP enabled and assign a fixed IP address directly in the router instead. Network settings only apply while the device is programmed. After an update, for example, the device may return to DHCP mode until it is programmed again.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
### LAN Mode
|
||||
|
||||
Select the desired mode for the LAN interface. The 10 MBit/s mode can be used to reduce power consumption.
|
||||
|
||||
**Note:** Newer switches with speeds of 2.5 GBit/s and above usually no longer provide a 10 MBit/s mode. In such cases, a connection may not be established. It is still worth trying because some devices support this mode.
|
||||
@@ -0,0 +1,3 @@
|
||||
### mDNS
|
||||
|
||||
The mDNS service resolves "Hostname.local" and can also be used later to discover OpenKNX devices automatically in the local network.
|
||||
@@ -0,0 +1,7 @@
|
||||
### NTP Client
|
||||
|
||||
Enabling the NTP client allows the device to retrieve the current time cyclically from a time server instead of obtaining it from the bus. If desired, the device can also send the current time to the bus.
|
||||
|
||||
The previous settings and communication objects for retrieving time from the bus are no longer used. Instead, three new communication objects are available to provide time, date, or both combined on the bus.
|
||||
|
||||
The time server (NTP server) used to retrieve the current time can also be changed. Usually no change is required because the preset server (pool.ntp.org) works reliably and is widely used. This server is an alias for a large number of time servers.
|
||||
@@ -0,0 +1,7 @@
|
||||
### OTA Update
|
||||
|
||||
Allows firmware to be updated directly without using KNX or a USB connection.
|
||||
|
||||
* **In programming mode:** The device must first be put into programming mode for an update, for example by pressing the PROG button.
|
||||
* **Always active:** Enables permanent update mode for the device. This mode is not recommended because devices can easily be confused with each other.
|
||||
* **Disabled:** Disables updates over the network.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### Web Server
|
||||
|
||||
The web server can be configured here in the future and then opened in a browser. This function is not currently integrated and is only a placeholder.
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
### WiFi Assistant
|
||||
|
||||
This WiFi assistant transfers WiFi credentials to the device. The hardware must use a WiFi adapter. Devices connected through an IP network must already have a WiFi connection and valid credentials. The assistant can therefore only change existing credentials. TP devices, however, can always be adjusted over the bus.
|
||||
|
||||
IP devices without WiFi credentials must initially be set up in another way. This depends on both the device software and the hardware used. Setup via terminal (USB) should always work. On the console, enter `wifi SSID PSK`.
|
||||
|
||||
On Arduino-Pico-based devices, WiFi credentials can usually also be transferred over USB. Connect the device to the computer and start transfer mode by double-clicking the Prog button. Then copy a file named "WIFI.TXT" to the removable drive. The first line must contain the SSID and the second line the PSK. Afterwards, exit the mode with another double-click and restart the device.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
### ドキュメント
|
||||
|
||||
完全なアプリケーション説明は次の場所で確認できます:https://github.com/openknx/OFM-Network/blob/v1/doc/Applikationsbeschreibung-Netzwerk.md
|
||||
|
||||
ネットワーク設定では、デバイスの IP アドレスを調整できるだけでなく、複数のサービスを有効または無効にできます。これには、現在時刻を取得する NTP クライアント、OpenKNX デバイスを自動検出する mDNS、ネットワーク経由でデバイスファームウェアを更新する機能が含まれます。
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### Web サーバー
|
||||
|
||||
将来的にここで Web サーバーを有効化し、ブラウザーから開けるようにできます。この機能は現在まだ統合されておらず、現時点ではプレースホルダーです。
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### ホスト名
|
||||
|
||||
ホスト名はシリアル番号から自動生成されます(OpenKNX-XXXXXXXX)。通常は変更不要です。カスタムホスト名を使用する場合、24 文字を超えてはいけません。使用できるのは文字、数字、ハイフンのみです。ホスト名は文字で始まり、ハイフンで終わってはいけません。
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
### IP アドレス
|
||||
|
||||
この入力フォームでは、IP アドレスを DHCP サーバーから動的に割り当てるか、手動で設定するかを選択できます。手動設定では、IP アドレスに加えてサブネットマスク、デフォルトゲートウェイ(ルーター)、ネームサーバー(DNS)が必要です。
|
||||
|
||||
**注意:** DHCP 設定を維持し、固定 IP アドレスはルーター側で割り当てることを推奨します。ネットワーク設定は、デバイスがプログラム済みの状態でのみ有効です。たとえば更新後は、再度プログラムされるまで DHCP モードに戻る場合があります。
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
### LAN モード
|
||||
|
||||
LAN インターフェイスの希望するモードを選択します。10 MBit/s モードは消費電力を抑えるために使用できます。
|
||||
|
||||
**注意:** 2.5 GBit/s 以上の速度に対応する新しいスイッチでは、通常 10 MBit/s モードが用意されていません。その場合、接続できない可能性があります。ただし、このモードをサポートするデバイスもあるため、試す価値はあります。
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### mDNS
|
||||
|
||||
mDNS サービスは "Hostname.local" を解決し、後でローカルネットワーク内の OpenKNX デバイスを自動検出するためにも使用できます。
|
||||
@@ -0,0 +1,7 @@
|
||||
### NTP クライアント
|
||||
|
||||
NTP クライアントを有効にすると、デバイスはバスからではなく、時刻サーバーから周期的に現在時刻を取得できます。必要に応じて、現在時刻をバスへ送信することもできます。
|
||||
|
||||
従来の、バスから時刻を取得するための設定や通信オブジェクトは使用されなくなります。代わりに、時刻、日付、またはその両方を結合してバスに提供する 3 つの新しい通信オブジェクトが利用できます。
|
||||
|
||||
現在時刻を取得する時刻サーバー(NTP サーバー)も変更できます。通常は変更不要です。既定のサーバー pool.ntp.org は信頼性が高く広く使われており、多数の時刻サーバーへのエイリアスとして機能します。
|
||||
@@ -0,0 +1,7 @@
|
||||
### OTA 更新
|
||||
|
||||
KNX や USB 接続を使用せずに、ファームウェアを直接更新できます。
|
||||
|
||||
* **プログラミングモード時:** 更新するには、事前にデバイスをプログラミングモードにする必要があります。たとえば PROG ボタンを押します。
|
||||
* **常に有効:** デバイスの常時更新モードを有効にします。デバイスを取り違えやすくなるため、このモードは推奨されません。
|
||||
* **無効:** ネットワーク経由の更新を無効にします。
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### Web サーバー
|
||||
|
||||
将来的にここで Web サーバーを設定し、ブラウザーから開けるようにできます。この機能は現在まだ統合されておらず、現時点ではプレースホルダーです。
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
### WiFi アシスタント
|
||||
|
||||
この WiFi アシスタントは、WiFi 認証情報をデバイスへ転送します。使用するハードウェアには WiFi アダプターが必要です。IP ネットワーク経由で接続されるデバイスは、すでに WiFi 接続と有効な認証情報を持っている必要があります。そのため、このアシスタントで変更できるのは既存の認証情報のみです。一方、TP デバイスは常にバス経由で調整できます。
|
||||
|
||||
WiFi 認証情報を持たない IP デバイスは、最初に別の方法でセットアップする必要があります。これはデバイスソフトウェアと使用するハードウェアの両方に依存します。ターミナル(USB)経由の設定は通常常に機能します。コンソールで `wifi SSID PSK` と入力してください。
|
||||
|
||||
Arduino-Pico ベースのデバイスでは、通常 USB 経由でも WiFi 認証情報を転送できます。デバイスをコンピューターに接続し、Prog ボタンをダブルクリックして転送モードを開始します。その後、"WIFI.TXT" という名前のファイルをリムーバブルドライブにコピーします。1 行目に SSID、2 行目に PSK を記載してください。最後にもう一度ダブルクリックしてモードを終了し、デバイスを再起動します。
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
### 文档
|
||||
|
||||
完整的应用说明可在以下地址查看:https://github.com/openknx/OFM-Network/blob/v1/doc/Applikationsbeschreibung-Netzwerk.md
|
||||
|
||||
网络设置不仅可用于调整设备的 IP 地址,还可启用或停用多种服务。其中包括用于获取当前时间的 NTP 客户端、用于自动发现 OpenKNX 设备的 mDNS,以及通过网络更新设备固件的功能。
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### Web 服务器
|
||||
|
||||
将来可在此启用 Web 服务器,并通过浏览器访问。此功能目前尚未集成,当前仅作为占位项。
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### 主机名
|
||||
|
||||
主机名会根据序列号自动生成(OpenKNX-XXXXXXXX),通常不需要调整。如果需要自定义,长度不得超过 24 个字符,并且只能包含字母、数字和连字符。主机名必须以字母开头,且不能以连字符结尾。
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
### IP 地址
|
||||
|
||||
在此表单中,你可以选择 IP 地址由 DHCP 服务器动态分配,或手动指定。手动配置时,除 IP 地址外,还需要子网掩码、默认网关(路由器)和名称服务器(DNS)。
|
||||
|
||||
**注意:** 建议保留 DHCP 设置,并在路由器中直接为设备分配固定 IP 地址。网络设置仅在设备已编程的状态下生效。例如更新后,设备可能会恢复到 DHCP 模式,直到再次编程。
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
### LAN 模式
|
||||
|
||||
选择 LAN 接口所需的模式。10 MBit/s 模式可用于降低功耗。
|
||||
|
||||
**注意:** 较新的交换机,特别是 2.5 GBit/s 及以上速度的交换机,通常不再提供 10 MBit/s 模式。在这种情况下可能无法建立连接。但仍值得尝试,因为部分设备支持该模式。
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### mDNS
|
||||
|
||||
mDNS 服务可解析 "Hostname.local",之后也可用于在本地网络中自动发现 OpenKNX 设备。
|
||||
@@ -0,0 +1,7 @@
|
||||
### NTP 客户端
|
||||
|
||||
启用 NTP 客户端后,设备可以周期性地从时间服务器获取当前时间,而不是从总线获取。如有需要,设备也可以将当前时间发送到总线。
|
||||
|
||||
此前用于从总线获取时间的设置和通信对象将不再使用。取而代之的是三个新的通信对象,可在总线上提供时间、日期,或时间日期组合。
|
||||
|
||||
也可以调整用于获取当前时间的时间服务器(NTP 服务器)。通常不需要修改,因为预设服务器 pool.ntp.org 可靠且使用广泛。该服务器是大量时间服务器的别名。
|
||||
@@ -0,0 +1,7 @@
|
||||
### OTA 更新
|
||||
|
||||
允许在不使用 KNX 或 USB 连接的情况下直接更新固件。
|
||||
|
||||
* **编程模式下:** 更新前必须先将设备置于编程模式,例如按下 PROG 按钮。
|
||||
* **始终启用:** 为设备启用永久更新模式。不建议使用该模式,因为容易混淆不同设备。
|
||||
* **已禁用:** 禁用通过网络进行更新。
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
### Web 服务器
|
||||
|
||||
将来可在此配置 Web 服务器,并通过浏览器访问。此功能目前尚未集成,当前仅作为占位项。
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
### WiFi 助手
|
||||
|
||||
此 WiFi 助手可将 WiFi 凭据传输到设备。前提是所用硬件带有 WiFi 适配器。通过 IP 网络连接的设备必须已经具备 WiFi 连接和有效凭据,因此该助手只能修改现有凭据。而 TP 设备始终可以通过总线进行调整。
|
||||
|
||||
没有 WiFi 凭据的 IP 设备必须先通过其他方式完成初始设置。这取决于设备软件和所用硬件。通过终端(USB)设置通常始终可用,只需在控制台输入 `wifi SSID PSK`。
|
||||
|
||||
对于基于 Arduino-Pico 的设备,通常也可以通过 USB 传输 WiFi 凭据。将设备连接到电脑,并双击 Prog 按钮启动传输模式。然后将名为 "WIFI.TXT" 的文件复制到可移动驱动器。第一行必须包含 SSID,第二行必须包含 PSK。之后再次双击退出该模式并重启设备。
|
||||
@@ -0,0 +1,33 @@
|
||||
function sendWifiSettings(device, online, progress, context) {
|
||||
progress.setText("Übertrage die WiFi-Einstellungen.");
|
||||
online.connect();
|
||||
|
||||
var data = [1];
|
||||
var ssid = device.getParameterByName("NET_WifiSSID").value;
|
||||
var psk = device.getParameterByName("NET_WifiPassword").value;
|
||||
|
||||
data[1] = ssid.length;
|
||||
data[2] = psk.length;
|
||||
|
||||
for (var i = 0; i < data[1]; ++i) {
|
||||
var code = ssid.charCodeAt(i);
|
||||
data = data.concat([code]);
|
||||
}
|
||||
data = data.concat(0); // null-terminated string
|
||||
|
||||
for (var i = 0; i < data[2]; ++i) {
|
||||
var code = psk.charCodeAt(i);
|
||||
data = data.concat([code]);
|
||||
}
|
||||
data = data.concat(0); // null-terminated string
|
||||
|
||||
var resp = online.invokeFunctionProperty(0xA0, 5, data);
|
||||
if (resp[0] != 0) {
|
||||
throw new Error("Fehler: Das verwendete Gerät unterstützt kein WiFi!");
|
||||
}
|
||||
|
||||
device.getParameterByName("NET_WifiSSID").value = "";
|
||||
device.getParameterByName("NET_WifiPassword").value = "";
|
||||
online.disconnect();
|
||||
progress.setText("Übertragung der WiFi-Einstellungen abgeschlossen.");
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml-model href="../../Organization/knxprod-support/knx_project_20/knx-editor.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<KNX xmlns:op="http://github.com/OpenKNX/OpenKNXproducer" xmlns="http://knx.org/xml/project/20" CreatedBy="KNX MT" ToolVersion="5.1.255.16695">
|
||||
<!-- Enable IPTool of ETS. This hide ip settings in network tab -->
|
||||
<op:config name="%NET_IPConfigTool%" value="0" />
|
||||
<op:config name="%NET_WifiTool%" value="0" />
|
||||
<op:config name="%NET_LanMode%" value="0" />
|
||||
<op:config name="%NET_ServiceNTP%" value="1" />
|
||||
<op:config name="%NET_ServiceOTA%" value="1" />
|
||||
<op:config name="%NET_ShowMediumInfo%" value="0" />
|
||||
|
||||
<!-- Prepare: Not ready to use -->
|
||||
<op:config name="%NET_ServiceHTTP%" value="0" />
|
||||
|
||||
|
||||
<ManufacturerData>
|
||||
<Manufacturer>
|
||||
<ApplicationPrograms>
|
||||
<ApplicationProgram>
|
||||
<Static>
|
||||
<ParameterTypes>
|
||||
<ParameterType Id="%AID%_PT-CustomHostname" Name="CustomHostname">
|
||||
<TypeRestriction Base="Value" SizeInBit="1">
|
||||
<Enumeration Text="Standard" Value="0" Id="%ENID%" />
|
||||
<Enumeration Text="Anpassen" Value="1" Id="%ENID%" />
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-HostAddress" Name="HostAddress">
|
||||
<TypeIPAddress AddressType="HostAddress" />
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-SubnetMask" Name="SubnetMask">
|
||||
<TypeIPAddress AddressType="SubnetMask" />
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-GatewayAddress" Name="GatewayAddress">
|
||||
<TypeIPAddress AddressType="GatewayAddress" />
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-Hostname" Name="Hostname" ValidationErrorRef="%AID%_M-%TT%00001">
|
||||
<TypeText SizeInBit="192" Pattern="^([A-Za-z](?:[A-Za-z0-9\-]*[A-Za-z0-9])*|)$"/>
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-WifiSSID" Name="WifiSSID">
|
||||
<TypeText SizeInBit="256" />
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-WifiPassword" Name="WifiPassword">
|
||||
<TypeText SizeInBit="504" />
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-OTAUpdate" Name="OTAUpdate">
|
||||
<TypeRestriction Base="Value" SizeInBit="2">
|
||||
<Enumeration Text="Im Prog-Modus" Value="0" Id="%ENID%" />
|
||||
<Enumeration Text="Immer aktiv" Value="1" Id="%ENID%" />
|
||||
<Enumeration Text="Abgeschaltet" Value="2" Id="%ENID%" />
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-LanMode" Name="LanMode">
|
||||
<TypeRestriction Base="Value" SizeInBit="4">
|
||||
<Enumeration Text="Automatisch" Value="0" Id="%ENID%" />
|
||||
<Enumeration Text="10 MBit/s (Stromsparender)" Value="3" Id="%ENID%" />
|
||||
<Enumeration Text="100 MBit/s" Value="1" Id="%ENID%" />
|
||||
<!-- <Enumeration Text="100 MBit/s Halb-Duplex" Value="2" Id="%ENID%" /> -->
|
||||
<!-- <Enumeration Text="10 MBit/s Halb-Duplex" Value="4" Id="%ENID%" /> -->
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
<ParameterType Id="%AID%_PT-NTPServer" Name="NTPServer">
|
||||
<TypeText SizeInBit="400" /> <!-- 50 bytes -->
|
||||
</ParameterType>
|
||||
<!-- Status LEDs-->
|
||||
<ParameterType Id="%AID%_PT-SLEDFunc" Name="SLEDFunc">
|
||||
<TypeRestriction Base="Value" SizeInBit="16" UIHint="DropDown">
|
||||
<Enumeration Text="Netzwerkstatus" Value="10" Id="%ENID%" />
|
||||
</TypeRestriction>
|
||||
</ParameterType>
|
||||
</ParameterTypes>
|
||||
<Parameters>
|
||||
<!-- helper to check against a certain value in the case of a choose -->
|
||||
<Parameter Id="%AID%_P-%TT%01000" Name="SelectHelper" ParameterType="%AID%_PT-ValueDpt5" Text="" Value="0" Access="None" />
|
||||
<Parameter Id="%AID%_P-%TT%01001" Name="WifiSSID" ParameterType="%AID%_PT-WifiSSID" Text="SSID" Value="" />
|
||||
<Parameter Id="%AID%_P-%TT%01002" Name="WifiPassword" ParameterType="%AID%_PT-WifiPassword" Text="PSK" Value=""/>
|
||||
|
||||
<!-- IP Config -->
|
||||
<Union SizeInBit="128"><!-- 16bytes-->
|
||||
<Memory CodeSegment="%MID%" Offset="0" BitOffset="0" />
|
||||
<Parameter Id="%AID%_UP-%TT%00011" Name="HostAddress" ParameterType="%AID%_PT-HostAddress" Offset="0" BitOffset="0" Text="IP-Adresse" Value="192.168.178.2" />
|
||||
<Parameter Id="%AID%_UP-%TT%00012" Name="SubnetMask" ParameterType="%AID%_PT-SubnetMask" Offset="4" BitOffset="0" Text="Subnetzsmaske" Value="255.255.255.0" />
|
||||
<Parameter Id="%AID%_UP-%TT%00013" Name="GatewayAddress" ParameterType="%AID%_PT-GatewayAddress" Offset="8" BitOffset="0" Text="Standardgateway" Value="192.168.178.1" />
|
||||
<Parameter Id="%AID%_UP-%TT%00021" Name="NameserverAddress" ParameterType="%AID%_PT-HostAddress" Offset="12" BitOffset="0" Text="Nameserver" Value="192.168.178.1" />
|
||||
<!-- Reserved for Nameserver 2 -->
|
||||
</Union>
|
||||
|
||||
<!-- 1bit options -->
|
||||
<Union SizeInBit="8">
|
||||
<Memory CodeSegment="%MID%" Offset="16" BitOffset="0" />
|
||||
|
||||
<Parameter Id="%AID%_UP-%TT%00001" Name="CustomHostname" ParameterType="%AID%_PT-OnOffYesNo" Offset="0" BitOffset="0" Text="Hostname anpassen" Value="0" />
|
||||
<Parameter Id="%AID%_UP-%TT%00002" Name="StaticIP" ParameterType="%AID%_PT-OnOffYesNoInverted" Offset="0" BitOffset="1" Text="DHCP" Value="0" />
|
||||
<!-- 6 bits free -->
|
||||
</Union>
|
||||
|
||||
<!-- Services -->
|
||||
<Union SizeInBit="8">
|
||||
<Memory CodeSegment="%MID%" Offset="17" BitOffset="0" />
|
||||
<Parameter Id="%AID%_UP-%TT%00031" Name="mDNS" ParameterType="%AID%_PT-OnOffYesNo" Offset="0" BitOffset="0" Text="mDNS" Value="1" />
|
||||
<Parameter Id="%AID%_UP-%TT%00032" Name="HTTP" ParameterType="%AID%_PT-OnOffYesNo" Offset="0" BitOffset="1" Text="Weberver" Value="1" />
|
||||
<Parameter Id="%AID%_UP-%TT%00033" Name="NTP" ParameterType="%AID%_PT-OnOffYesNo" Offset="0" BitOffset="2" Text="NTP-Client" Value="0" />
|
||||
<Parameter Id="%AID%_UP-%TT%00034" Name="OTAUpdate" ParameterType="%AID%_PT-OTAUpdate" Offset="0" BitOffset="3" Text="OTA-Update" Value="0" />
|
||||
<!-- 5 bits free -->
|
||||
</Union>
|
||||
|
||||
<!-- Hostname -->
|
||||
<Parameter Id="%AID%_P-%TT%00010" Name="HostName" ParameterType="%AID%_PT-Hostname" Text="Hostname" Value="" >
|
||||
<Memory CodeSegment="%MID%" Offset="18" BitOffset="0" />
|
||||
<!-- 192bits / 24bytes-->
|
||||
</Parameter>
|
||||
|
||||
<!-- Type & Mode -->
|
||||
<Union SizeInBit="8">
|
||||
<Memory CodeSegment="%MID%" Offset="59" BitOffset="0" />
|
||||
<Parameter Id="%AID%_UP-%TT%00052" Name="LanMode" ParameterType="%AID%_PT-LanMode" Offset="0" BitOffset="0" Text="LAN-Modus" Value="0" />
|
||||
</Union>
|
||||
|
||||
<!-- NTP -->
|
||||
<Union SizeInBit="408">
|
||||
<Memory CodeSegment="%MID%" Offset="60" BitOffset="0" />
|
||||
<Parameter Id="%AID%_UP-%TT%00061" Name="NTPServer" ParameterType="%AID%_PT-NTPServer" Offset="0" BitOffset="0" Text="Zeitserver" Value="pool.ntp.org" />
|
||||
<!-- 50 characters + 0 terminator = 51 bytes used -->
|
||||
</Union>
|
||||
|
||||
</Parameters>
|
||||
<ParameterRefs>
|
||||
<ParameterRef Id="%AID%_P-%TT%01000_R-%TT%0100000" RefId="%AID%_P-%TT%01000" />
|
||||
<ParameterRef Id="%AID%_P-%TT%01001_R-%TT%0100100" RefId="%AID%_P-%TT%01001" />
|
||||
<ParameterRef Id="%AID%_P-%TT%01002_R-%TT%0100200" RefId="%AID%_P-%TT%01002" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00001_R-%TT%0000101" RefId="%AID%_UP-%TT%00001" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00002_R-%TT%0000201" RefId="%AID%_UP-%TT%00002" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00034_R-%TT%0003401" RefId="%AID%_UP-%TT%00034" />
|
||||
<ParameterRef Id="%AID%_P-%TT%00010_R-%TT%0001001" RefId="%AID%_P-%TT%00010" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00011_R-%TT%0001101" RefId="%AID%_UP-%TT%00011" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00012_R-%TT%0001201" RefId="%AID%_UP-%TT%00012" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00013_R-%TT%0001301" RefId="%AID%_UP-%TT%00013" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00021_R-%TT%0002101" RefId="%AID%_UP-%TT%00021" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00031_R-%TT%0003101" RefId="%AID%_UP-%TT%00031" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00032_R-%TT%0003201" RefId="%AID%_UP-%TT%00032" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00033_R-%TT%0003301" RefId="%AID%_UP-%TT%00033" />
|
||||
<ParameterRef Id="%AID%_UP-%TT%00052_R-%TT%0005201" RefId="%AID%_UP-%TT%00052" />
|
||||
<!-- NTPServer -->
|
||||
<ParameterRef Id="%AID%_UP-%TT%00061_R-%TT%0006101" RefId="%AID%_UP-%TT%00061" />
|
||||
</ParameterRefs>
|
||||
<ComObjectTable>
|
||||
</ComObjectTable>
|
||||
<ComObjectRefs>
|
||||
</ComObjectRefs>
|
||||
<Messages>
|
||||
<Message Id="%AID%_M-%TT%00001" Name="InvalidHostName" Text="Darf nur 24 Zeichen lang sein und muss aus Buchstaben, Zahlen und Bindestriche bestehen." />
|
||||
</Messages>
|
||||
<Script>
|
||||
<op:include href="Network.script.js" prefix="NET" type="script" />
|
||||
</Script>
|
||||
</Static>
|
||||
<Dynamic>
|
||||
<Channel Id="%AID%_CH-%PREFIX%" Number="%PREFIX%" Name="Main" Text="Netzwerk" Icon="ethernet" HelpContext="NET-Dokumentation">
|
||||
<ParameterBlock Id="%AID%_PB-nnn" Name="Basic" Text="Allgemein" Icon="cog-outline" HelpContext="NET-Dokumentation">
|
||||
<!-- Hidden -->
|
||||
<ParameterRefRef RefId="%AID%_P-%TT%01000_R-%TT%0100000" />
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Netzwerk" UIHint="Headline" />
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Version: %ModuleVersion%" />
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="" UIHint="HorizontalRuler" />
|
||||
<choose ParamRefId="%AID%_P-%TT%01000_R-%TT%0100000"><!-- Static 0 -->
|
||||
<when test="!=%NET_ShowMediumInfo%">
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Diese Einstellungen sind lediglich für Geräte mit IP-Schnittstelle vorgesehen.
|
||||
Geräte, die ausschließlich per TP angebunden sind, benötigen hier keine Konfiguration" UIHint="Information" />
|
||||
</when>
|
||||
</choose>
|
||||
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="IP-Adresse" UIHint="Headline" />
|
||||
<choose ParamRefId="%AID%_P-%TT%01000_R-%TT%0100000"><!-- Static 0 -->
|
||||
<when test="%NET_IPConfigTool%"> <!-- default -->
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00002_R-%TT%0000201" IndentLevel="1" HelpContext="NET-IP-Adresse" />
|
||||
<choose ParamRefId="%AID%_UP-%TT%00002_R-%TT%0000201">
|
||||
<when test="1">
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00011_R-%TT%0001101" IndentLevel="1" HelpContext="NET-IP-Adresse" />
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00012_R-%TT%0001201" IndentLevel="1" HelpContext="NET-IP-Adresse" />
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00013_R-%TT%0001301" IndentLevel="1" HelpContext="NET-IP-Adresse" />
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00021_R-%TT%0002101" IndentLevel="1" HelpContext="NET-IP-Adresse" />
|
||||
</when>
|
||||
</choose>
|
||||
</when>
|
||||
<when test="!=%NET_IPConfigTool%"> <!-- router -->
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="IP-Einstellungen können im Eigenschaften Feld der ETS, Tab IP gesetzt werden." UIHint="Information" />
|
||||
</when>
|
||||
</choose>
|
||||
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Services" UIHint="Headline" />
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00031_R-%TT%0003101" IndentLevel="1" HelpContext="NET-MDNS" />
|
||||
|
||||
<!-- HTTP -->
|
||||
<choose ParamRefId="%AID%_P-%TT%01000_R-%TT%0100000">
|
||||
<when test="!=%NET_ServiceHTTP%">
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00032_R-%TT%0003201" IndentLevel="1" HelpContext="NET-HTTP" />
|
||||
</when>
|
||||
</choose>
|
||||
|
||||
<!-- NTP -->
|
||||
<choose ParamRefId="%AID%_P-%TT%01000_R-%TT%0100000">
|
||||
<when test="!=%NET_ServiceNTP%">
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00033_R-%TT%0003301" IndentLevel="1" HelpContext="NET-NTP" />
|
||||
<choose ParamRefId="%AID%_UP-%TT%00033_R-%TT%0003301">
|
||||
<when test="0">
|
||||
<Assign TargetParamRefRef="%AID%_UP-1000006_R-100000601" Value="0" />
|
||||
</when>
|
||||
<when test="1">
|
||||
<Assign TargetParamRefRef="%AID%_UP-1000006_R-100000601" Value="1" />
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00061_R-%TT%0006101" IndentLevel="1" HelpContext="NET-NTP" />
|
||||
</when>
|
||||
</choose>
|
||||
</when>
|
||||
<when test="%NET_ServiceNTP%">
|
||||
<Assign TargetParamRefRef="%AID%_UP-1000006_R-100000601" Value="0" />
|
||||
</when>
|
||||
</choose>
|
||||
|
||||
<!-- OTA -->
|
||||
<choose ParamRefId="%AID%_P-%TT%01000_R-%TT%0100000">
|
||||
<when test="!=%NET_ServiceOTA%">
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00034_R-%TT%0003401" IndentLevel="1" HelpContext="NET-OTA" />
|
||||
</when>
|
||||
</choose>
|
||||
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Erweiterte Einstellung" UIHint="Headline" />
|
||||
<!-- Hostname -->
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00001_R-%TT%0000101" IndentLevel="1" HelpContext="NET-Hostname" />
|
||||
<choose ParamRefId="%AID%_UP-%TT%00001_R-%TT%0000101">
|
||||
<when test="1">
|
||||
<ParameterRefRef RefId="%AID%_P-%TT%00010_R-%TT%0001001" IndentLevel="1" HelpContext="NET-Hostname" />
|
||||
</when>
|
||||
</choose>
|
||||
|
||||
<!-- LanMode -->
|
||||
<choose ParamRefId="%AID%_P-%TT%01000_R-%TT%0100000">
|
||||
<when test="!=%NET_LanMode%">
|
||||
<ParameterRefRef RefId="%AID%_UP-%TT%00052_R-%TT%0005201" IndentLevel="1" HelpContext="%DOC%" />
|
||||
</when>
|
||||
</choose>
|
||||
|
||||
</ParameterBlock>
|
||||
|
||||
<!-- HTTP -->
|
||||
<choose ParamRefId="%AID%_UP-%TT%00032_R-%TT%0003201">
|
||||
<when test="1">
|
||||
<ParameterBlock Id="%AID%_PB-nnn" Name="HTTP" Text="Webserver" Icon="web" HelpContext="NET-Webserver">
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Webserver" UIHint="Headline" />
|
||||
</ParameterBlock>
|
||||
</when>
|
||||
</choose>
|
||||
|
||||
<!-- WiFi -->
|
||||
<choose ParamRefId="%AID%_P-%TT%01000_R-%TT%0100000"><!-- Static 0 -->
|
||||
<when test="!=%NET_WifiTool%"> <!-- default -->
|
||||
<ParameterBlock Id="%AID%_PB-nnn" Name="WiFi" Text="WiFi-Assistent" Icon="wifi" HelpContext="NET-WiFi-Assistent">
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="Dieser WiFi-Assistent ermöglicht das Übertragen von WiFi-Zugangsdaten auf das Gerät.
|
||||
Diese Einstellungen sind lediglich für Geräte mit WiFi-Schnittstelle vorgesehen." UIHint="Information" />
|
||||
|
||||
<ParameterSeparator Id="%AID%_PS-nnn" Text="WiFi-Zugangsdaten" UIHint="Headline" />
|
||||
<ParameterRefRef RefId="%AID%_P-%TT%01001_R-%TT%0100100" IndentLevel="1" />
|
||||
<ParameterRefRef RefId="%AID%_P-%TT%01002_R-%TT%0100200" IndentLevel="1" />
|
||||
|
||||
<ParameterBlock Id="%AID%_PB-nnn" Name="A" Inline="true" Layout="Grid">
|
||||
<Rows>
|
||||
<Row Id="%AID%_PB-nnn_R-1" Name="Row1" />
|
||||
</Rows>
|
||||
<Columns>
|
||||
<Column Id="%AID%_PB-nnn_C-1" Name="Col1" Width="46%" />
|
||||
<Column Id="%AID%_PB-nnn_C-2" Name="Col2" Width="53%" />
|
||||
</Columns>
|
||||
<Button Id="%AID%_MD-%TT%000100_B-%TT%00000" Text="Übertragen" EventHandlerOnline="ConnectionOriented" EventHandler="sendWifiSettings" EventHandlerParameters="{ "Module": %TT% }" Cell="1,2" />
|
||||
</ParameterBlock>
|
||||
|
||||
</ParameterBlock>
|
||||
</when>
|
||||
</choose>
|
||||
|
||||
</Channel>
|
||||
</Dynamic>
|
||||
</ApplicationProgram>
|
||||
</ApplicationPrograms>
|
||||
<Baggages>
|
||||
<Baggage TargetPath="" Name="Help_en.zip" Id="%FILE-HELP-de%">
|
||||
<FileInfo TimeInfo="%DATETIME%" />
|
||||
</Baggage>
|
||||
<Baggage TargetPath="" Name="Help_de.zip" Id="%FILE-HELP-de-DE%">
|
||||
<FileInfo TimeInfo="%DATETIME%" />
|
||||
</Baggage>
|
||||
<Baggage TargetPath="" Name="Help_zh-CN.zip" Id="%FILE-HELP-zh-CN%">
|
||||
<FileInfo TimeInfo="%DATETIME%" />
|
||||
</Baggage>
|
||||
<Baggage TargetPath="" Name="Help_ja-JP.zip" Id="%FILE-HELP-ja-JP%">
|
||||
<FileInfo TimeInfo="%DATETIME%" />
|
||||
</Baggage>
|
||||
<Baggage TargetPath="" Name="Icons.zip" Id="%FILE-ICONS%">
|
||||
<FileInfo TimeInfo="%DATETIME%" />
|
||||
</Baggage>
|
||||
</Baggages>
|
||||
</Manufacturer>
|
||||
</ManufacturerData>
|
||||
</KNX>
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "OGM-Common",
|
||||
"version": "1.7.2",
|
||||
"dependencies": {
|
||||
"khoih-prog/TimerInterrupt_Generic": "^1.13.0",
|
||||
"nickgammon/Regexp": "^0.1.0",
|
||||
"robtillaart/ANSI": "^0.2.0",
|
||||
"RTTStream": "https://github.com/koendv/RTTStream#1.4.0"
|
||||
},
|
||||
"description": "Common routines and helpers, used in OpenKNX projects",
|
||||
"homepage": "https://openknx.de",
|
||||
"authors": {
|
||||
"name": "OpenKNX",
|
||||
"email": "info@openknx.de",
|
||||
"url": "https://openknx.de",
|
||||
"maintainer": true
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/OpenKNX/OGM-Common"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
### Bei Neustart vom Bus lesen
|
||||
|
||||
Nach einem Neustart können Uhrzeit und Datum auch aktiv über Lesetelegramme abgefragt werden. Mit diesem Parameter wird bestimmt, ob Uhrzeit und Datum nach einem Neustart aktiv gelesen werden.
|
||||
|
||||
Wenn dieser Parameter gesetzt ist, wird die Uhrzeit und das Datum alle 20-30 Sekunden über ein Lesetelegramm vom Bus gelesen, bis eine entsprechende Antwort kommt. Falls keine Uhr im KNX-System vorhanden ist oder die Uhr nicht auf Leseanfragen antworten kann, sollte dieser Parameter auf "Nein" gesetzt werden.
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
### Beschreibung
|
||||
|
||||
Der hier angegebene Name wird an verschiedenen Stellen verwendet, um diesen Kanal wiederzufinden.
|
||||
|
||||
* Seitenbeschreibung des Kanals
|
||||
* Name vom Kommunikationsobjekt
|
||||
|
||||
Eine aussagekräftige Benennung erlaubt eine einfachere Orientierung innerhalb der Applikation, vor allem wenn man viele Kanäle nutzt.
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
### Kommentar
|
||||
|
||||
Hier kann man einen Freitext eingeben, der den Kanal beschreibt. Dieser Text kann mehrzeilig sein. Leider unterstütz die ETS von sich aus keine mehrzeiligen Texte. Mit dem Button unter der Textbox kann man alle Eingaben der Zeichenfolge '\n' in neue Zeilen umwandeln lassen.
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
### Diagnoseobjekt anzeigen
|
||||
|
||||
Man kann bei diesem Gerät ein Diagnoseobjekt (KO 7) einschalten. Dieses Diagnoseobjekt ist primär zum Debuggen vorhanden, kann aber auch einem User bei einigen Fragen weiter helfen.
|
||||
|
||||
Die Grundidee vom Diagnoseobjekt: Man sendet mit der ETS Kommandos an das KO 7 und bekommt eine entsprechende Antwort. Derzeit sind nur wenige Kommandos für die Nutzung durch den Enduser geeignet, allerdings werden im Laufe der Zeit immer weitere Kommandos hinzukommen. Die Kommandos sind von den verwendeten OpenKNX-Modulen abhängig und werden in den dortigen Applikationsbeschreibungen beschrieben.
|
||||
|
||||
Mit einem 'Ja' wird das KO 7 'Diagnoseobjekt' freigeschaltet.
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
### Empfangen über
|
||||
|
||||
Dieses Gerät kann Uhrzeit und Datum vom Bus empfangen. Dabei kann man wählen, ob man Uhrzeit über ein Kommunikationsobjekt und das Datum über ein anders empfangen will oder beides, Uhrzeit und Datum, über ein kombiniertes Kommunikationsobjekt.
|
||||
|
||||
#### Ein kombiniertes KO
|
||||
|
||||
Wählt man diesen Punkt, wird ein kombiniertes Kommunikationsobjekt für Uhrzeit/Datum (DPT 19) bereitgestellt. Der KNX-Zeitgeber im System muss die kombinierte Uhrzeit/Datum entsprechend liefern können.
|
||||
|
||||
#### Zwei getrennte KOs
|
||||
|
||||
Wählt man diesen Punkt, wird je ein Kommunikationsobjekt für Uhrzeit (DPT 10) und Datum (DPT 11) bereitgestellt. Der KNX-Zeitgeber im System muss die Uhrzeit und das Datum für die beiden Kommunikationsobjekte liefern können.
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
### Erweitertes "In Betrieb"
|
||||
|
||||
Der erweiterte ‚In-Betrieb‘-Modus liefert zusätzliche Informationen zum Gerätestatus.
|
||||
Statt als einzelnes Bit (DPT-1) wird der Status nun als Byte (DPT-5) übertragen.
|
||||
Der erweiterte Status wird nicht nur zyklisch, sondern auch bei Änderungen gesendet – so können Probleme wie Netzwerkfehler oder Übertemperatur sofort gemeldet werden.
|
||||
Durch eine Bitmaske lassen sich dabei verschiedene Zustandsinformationen gezielt auswerten.
|
||||
|
||||
Struktur: `0b NRRR_TWSB`
|
||||
|
||||
* Das Bit **B** (`1`) repräsentiert das normale Signal "In Betrieb" (immer aktiv).
|
||||
* Das Bit **S** (`2`) repräsentiert den Startvorgang und wird einmalig nach Ablauf der Startverzögerung übermittelt.
|
||||
* Das Bit **W** (`4`) repräsentiert, ob das Gerät durch einen Watchdog neu gestartet wurde und wird nur in Verbindung mit dem Startup-Bit einmalig gesendet.
|
||||
* Das Bit **T** (`8`) repräsentiert, ob die BCU einen Übertemperaturalarm hat.
|
||||
* Das Bit **R** (`16`) repräsentiert, eine Reserve.
|
||||
* Das Bit **R** (`32`) repräsentiert, eine Reserve.
|
||||
* Das Bit **R** (`64`) repräsentiert, eine Reserve.
|
||||
* Das Bit **N** (`128`) repräsentiert, ob eine Netzwerkverbindung besteht.
|
||||
|
||||
**Hinweis:** Wenn eine neue Firmware auf das Gerät übertragen wird, kommt es in manchen Fällen dazu, dass das Flag für den "Neustart durch den Watchdog" gesetzt wurde.
|
||||
|
||||
**Tipp:** Bei Bedarf kann das Logikmodul daraus einzelne 1-Bit-KOs erzeugen. Ein entsprechendes Beispiel lässt sich über den Konfigurationstransfer importieren und anschließend über Eingang 2 anpassen.
|
||||
|
||||
```
|
||||
OpenKNX,cv1,*/LOG/*§f~Name=Bit%20aus%20erweitertem%20Betrieb%20ausmakieren§f~Logic=1§f~Calculate=1§f~Trigger=1§f~TriggerE1=1§f~NameInput1=Erweiterter%20Betriebsstatus§f~E1=1§f~E1Dpt=2§f~E1OtherKO:2=1§f~E1UseOtherKO=1§f~E1LowDpt5:1=0§f~NameInput2=Bitmaske%20(dezimal)§f~E2ConvertInt=5§f~E2=1§f~E2Dpt=2§f~E2LowDpt5Fix=128§f~NameOutput=ausmaskiertes%20Bit§f~OOn=8§f~OOnAll=8§f~OOnFunction=9§>Wert für Eingang 2 passend setzen!§;OpenKNX
|
||||
```
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
### Erweitertes Speichern
|
||||
|
||||
Die integrierten Module können standardmäßig ihre Zustände automatisch auf dem internen Flashspeicher zwischenspeichern. Dies erfolgt beim Ausfall der Busspannung (bei TP-Geräten mit entsprechendem SAVEPIN) und bei einem Neustart des Geräts. Einige Updateskripte triggern außerdem das Speichern vor dem Aktualisieren.
|
||||
|
||||
Bei einem Reset durch den Watchdog oder die Reset-Taste, bei einem Absturz oder bei einem Stromausfall (ohne entsprechenden SAVEPIN), kann das rechtzeitige Speichern jedoch nicht mehr durchgeführt werden. Hier bietet sich bei Bedarf an, die Daten zyklisch oder manuell (per KO) zu speichern. Folgende Punkte sind zu beachten:
|
||||
|
||||
#### Flashspeicher
|
||||
Ein Flashspeicher unterliegt begrenzten Schreibzyklen. Ein zu häufiges Speichern führt zu einer verkürzten Lebensdauer. Die Anzahl der Schreibzyklen sind Flashspeicher abhängig. Eine pauschale Aussage zur Beständigkeit kann somit nicht getroffen werden. Allerdings kann man bei einem RP2040 davon ausgehen, dass dieser ca. 100000 Schreibzyklen verkraftet. Um den Flashspeicher zu schützen, kann man beim zyklischen Speichern maximal "Stündlich" auswählen. Unsere Empfehlung ist aber **nicht** mehr als 4x pro Tag. Beim manuellen Speichern gibt es ebenfalls einen zeitlichen Schreibschutz.
|
||||
|
||||
#### Auswirkung beim RP2040/RP2350
|
||||
|
||||
Bei einem RP2040/RP2350 wird während des Schreibvorgangs die Verarbeitung pausiert.
|
||||
Während dieser Pause können KNX-Telegramme verloren gehen. Daher sollte man sich gut überlegen, ob ein zyklisches Schreiben nötig ist. Wir empfehlen diese Option nur zu verwenden, wenn dies tatsächlich nötig ist (z.B. beim Zählermodul). Alternativ ist auch das manuelle Speichern per KO möglich, so dass man dies erst bei einer Änderung auslöst. Außerdem kann man mithilfe einer Zeitschaltuhr das zyklische Schreiben in die Nacht verlegen.
|
||||
|
||||
#### Zyklisches speichern
|
||||
|
||||
Auswahl:
|
||||
|
||||
- Deaktiviert
|
||||
- Jede Stunde
|
||||
- Alle 2 Stunden
|
||||
- Alle 4 Stunden
|
||||
- Alle 6 Stunden
|
||||
- Täglich
|
||||
- Wöchentlich
|
||||
|
||||
#### Manuelles speichern
|
||||
|
||||
Über diese Einstellung kann ein Gruppenobjekt eingeblendet werden, über das die Speicherung über Bus Telegramm mit dem Wert 1 ausgelöst werden kann.
|
||||
|
||||
Auswahl:
|
||||
|
||||
- Deaktiviert
|
||||
- Aktiv mit 5 min. Schreibschutz
|
||||
Die Anzahl der Speicheroperation werden auf maximal einmal pro 5 Minuten begrenzt
|
||||
- Aktiv mit 15 min. Schreibschutz
|
||||
Die Anzahl der Speicheroperation werden auf maximal einmal pro 15 Minuten begrenzt
|
||||
- Aktiv mit 60 min. Schreibschutz
|
||||
Die Anzahl der Speicheroperation werden auf maximal einmal pro 60 Minuten begrenzt
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
### Gerätestandort
|
||||
|
||||
Für die korrekte Berechnung der Zeit für Sonnenauf- und -untergang werden die genauen Koordinaten des Standorts benötigt sowie auch die Zeitzone und die Information, ob gerade die Sommerzeit aktiv ist.
|
||||
|
||||
Die Geo-Koordinaten können bei Google Maps nachgeschaut werden, indem man mit der rechten Maustaste auf das Objekt klickt und die unten erscheinenden Koordinaten benutzt.
|
||||
|
||||
Die Standard-Koordinaten stehen für Frankfurt am Main, Innenstadt.
|
||||
|
||||
### Breitengrad
|
||||
|
||||
In dem Feld wird der Breitengrad des Standortes eingegeben.
|
||||
|
||||
### Längengrad
|
||||
|
||||
In dem Feld wird der Längengrad des Standortes eingegeben.
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
### In Betrieb senden alle
|
||||
|
||||
Das Gerät kann einen Status "Ich bin noch in Betrieb" über das KO 1 senden.
|
||||
Diese Option ermöglicht das periodische Senden einer Nachricht. Dadurch kann überprüft werden, ob ein Gerät noch funktioniert und erreichbar ist.
|
||||
|
||||
Hier wird das Sendeintervall eingestellt.
|
||||
|
||||
Sollte hier eine 0 angegeben werden, wird kein "In Betrieb"-Signal gesendet und das KO 1 steht nicht zur Verfügung.
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
### Info-LEDs
|
||||
|
||||
Auf dieser Seite können die Info-LEDs angepasst werden. In der Regel ist bereits eine geräteabhängige Vorbelegung der LEDs vorhanden. Dies bedeutet jedoch nicht, dass jeder Info-LED bereits eine Funktion zugewiesen ist.
|
||||
|
||||
Diese Vorbelegung kann – sofern vorhanden – bei Bedarf angepasst werden. Da viele Produktdatenbanken geräteunabhängig aufgebaut sind, können unter Umständen mehr LEDs zur Auswahl stehen, als das verwendete Gerät tatsächlich bietet. In diesem Fall bleibt die entsprechende Zuordnung ohne Funktion.
|
||||
|
||||
Eine Beschreibung der LED-Funktionen ist im Wiki unter http://go.openknx.de/statusled zu finden.
|
||||
|
||||
**Hinweis**: Die Nummerierung der Info-LEDs entspricht nicht immer der Beschriftung auf der Gerätefront. Bei OpenKNX-REG1-Geräten z. B. beginnen die LEDs technisch von unten mit der Prog-LED, gefolgt von Info-LED 1 bis 3. Je nach verwendeter Front erfolgt die Beschriftung jedoch von oben mit Info 1, Info 2, Func und Prog-LED. Info 1 entspricht somit in Wirklichkeit der Info-LED 3, während Func in Wirklichkeit der Info-LED 1 entspricht.
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
### Modul aktivieren
|
||||
|
||||
Ist die Checkbox ausgewählt, ist das entsprechende Modul aktiv und dessen Parameterseite erscheint in der ETS.
|
||||
|
||||
Wird die Checkbox ausgeschaltet, wird das Module deaktiviert und alles Grupppenadressenzuordnungen entfernt. Die eingestellten Parameter bleiben erhalten, sind aber wirkungslos, da das Modul auf dem Gerät nicht ausgeführt wird.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
### Module
|
||||
|
||||
Hier wird eine Liste aller in dieser Applikation enthaltenen OpenKNX-Module und deren Version angezeigt. Standardmäßig sind alle Module aktiv. Mit der Checkbox kann man ein Modul deaktivieren. Es erscheint dann nicht mehr zur Auswahl in der ETS-Applikation und wird auf dem Gerät nicht ausgeführt.
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
### OpenKNX
|
||||
|
||||
OpenKNX ist eine offene Gemeinschaft von Hobbyisten die freie und quelloffene Software für KNX-Geräte erstellen. Um eine nachhaltige und professionelle Integration ins Smarthome zu erreichen streben wir eine weitgehende Kompatibilität zum KNX-Standard an. Mit OpenKNX hast Du die Möglichkeit bereits fertige Lösungen einzusetzen, diese individuell anzupassen oder ganz neue Lösungen zu realisieren - der modulare Ansatz bietet schnelle Erfolge durch den Einsatz bewährter Softwaremodule.
|
||||
|
||||
Weitere Informationen findest Du unter: www.openknx.de - wiki.openknx.de - forum.openknx.de
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
### POSIX TZ-String
|
||||
|
||||
**Allgemeiner Aufbau:**
|
||||
|
||||
`STD[+/-]hh[:mm[:ss]][DST[+/-]hh[:mm[:ss]][,Start[/Time],End[/Time]]]`
|
||||
|
||||
**Bedeutung der einzelnen Teile:**
|
||||
|
||||
- `STD`
|
||||
Abkürzung der Standardzeit (z. B. `CET` für Mitteleuropäische Zeit).
|
||||
|
||||
- `[+/-]hh[:mm[:ss]]`
|
||||
Zeitverschiebung zur UTC. Positive Werte sind westlich von Greenwich (z. B. USA), negative Werte östlich (z. B. Europa).
|
||||
Beispiel: `-1` für Mitteleuropa (eine Stunde östlich von UTC).
|
||||
|
||||
- `DST`
|
||||
Abkürzung der Sommerzeit (z. B. `CEST` für Mitteleuropäische Sommerzeit).
|
||||
|
||||
- `[+/-]hh[:mm[:ss]]`
|
||||
(Optional) Abweichung der Sommerzeit zur Standardzeit.
|
||||
|
||||
- `,Start[/Time],End[/Time]`
|
||||
(Optional) Regeln, wann die Sommerzeit beginnt und endet.
|
||||
Format: `M<m>.<w>.<d>` (Monat, Woche, Wochentag), z. B. `M3.5.0` = letzter Sonntag im März.
|
||||
|
||||
|
||||
**Beispiel für Mitteleuropa (Deutschland):**
|
||||
|
||||
`CET-1CEST,M3.5.0/2:00:00,M10.5.0/3:00:00`
|
||||
|
||||
- `CET` = Standardzeit (Central European Time)
|
||||
- `-1` = 1 Stunde östlich von UTC
|
||||
- `CEST` = Sommerzeit (Central European Summer Time)
|
||||
- `M3.5.0/2:00:00` = Sommerzeit beginnt am letzten Sonntag im März um 2:00 Uhr
|
||||
- `M10.5.0/3:00:00` = Sommerzeit endet am letzten Sonntag im Oktober um 3:00 Uhr
|
||||
|
||||
|
||||
**Weitere Beispiele:**
|
||||
|
||||
- UTC (keine Sommerzeit):
|
||||
`UTC0`
|
||||
|
||||
- New York (USA, mit Sommerzeit):
|
||||
`EST5EDT,M3.2.0/2,M11.1.0/2`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user