Refactor GatewayModbus and GatewayNetwork components

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

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-13 12:36:16 +08:00
parent df1dd472cc
commit b74367e5a0
18 changed files with 2244 additions and 609 deletions
+15 -3
View File
@@ -4,6 +4,8 @@ This folder hosts the native ESP-IDF C++ rewrite of the Lua DALI gateway.
## Layout
This is the list of top-level directories and their purposes,
update as the project evolves:
- `apps/`: standard ESP-IDF applications for each firmware role.
- `apps/gateway/main/Kconfig.projbuild`: project-visible gateway-role settings such as per-channel native/serial PHY selection, gateway ids, pin mapping, and startup transport policy.
- `components/`: reusable components shared by all gateway applications.
@@ -17,18 +19,28 @@ This folder hosts the native ESP-IDF C++ rewrite of the Lua DALI gateway.
- `gateway_bacnet/`: BACnet/IP server adapter backed by bacnet-stack, including the gateway-owned BACnet bridge model adapter.
- `gateway_ble/`: NimBLE GATT bridge for BLE transport parity on `FFF1`/`FFF2`/`FFF3`, including raw DALI notifications.
- `gateway_controller/`: Lua-compatible gateway command dispatcher, internal scene/group state, and notification fan-out.
- `gateway_network/`: HTTP `/info`, `/dali/cmd`, `/led/1`, `/led/0`, `/jq.js`, UDP port `2020` command/notify routing, Wi-Fi STA lifecycle, W5500 SPI Ethernet startup/teardown, ESP-Touch smartconfig, setup AP mode, ESP-NOW setup ingress, and BOOT-button Wi-Fi reset for the native gateway.
- `gateway_network/`: HTTP `/info`, `/dali/cmd`, `/led/1`, `/led/0`, `/jq.js`, UDP port `2020` command/notify routing, Wi-Fi STA lifecycle, W5500 SPI Ethernet startup/teardown, ESP-Touch smartconfig, setup AP mode, ESP-NOW setup ingress, setup AP GPIO handling, and optional Wi-Fi reset GPIO handling for the native gateway.
- `gateway_runtime/`: persistent runtime state, command queueing, and device info services.
- `gateway_485_control/`: optional 485 Lua control bridge for framed `0x28 0x01` commands and `0x22 ... checksum` notifications at `9600 8N1`; disabled by default because UART0 must be moved off the ESP-IDF console first.
- `gateway_usb_setup/`: optional USB Serial/JTAG setup bridge; disabled by default so USB remains available for debug at boot.
- `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.
## 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, and BOOT-button Wi-Fi credential clearing, 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.
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.
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.
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 default IO routing for that UART, not disabled. 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.
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 `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.
+82 -17
View File
@@ -71,18 +71,20 @@ config GATEWAY_CHANNEL1_NATIVE_BAUDRATE
config GATEWAY_CHANNEL1_SERIAL_TX_PIN
int "Serial PHY TX pin"
depends on GATEWAY_CHANNEL1_PHY_UART1 || GATEWAY_CHANNEL1_PHY_UART2
range 0 48
range -1 48
default 1
help
ESP32-S3 GPIO used by the channel 1 serial PHY transmit pin.
ESP32-S3 GPIO used by the channel 1 serial PHY transmit pin. Set to -1
to keep the UART driver's default TX routing.
config GATEWAY_CHANNEL1_SERIAL_RX_PIN
int "Serial PHY RX pin"
depends on GATEWAY_CHANNEL1_PHY_UART1 || GATEWAY_CHANNEL1_PHY_UART2
range 0 48
range -1 48
default 2
help
ESP32-S3 GPIO used by the channel 1 serial PHY receive pin.
ESP32-S3 GPIO used by the channel 1 serial PHY receive pin. Set to -1
to keep the UART driver's default RX routing.
config GATEWAY_CHANNEL1_SERIAL_BAUDRATE
int "Serial PHY baudrate"
@@ -178,18 +180,20 @@ config GATEWAY_CHANNEL2_NATIVE_BAUDRATE
config GATEWAY_CHANNEL2_SERIAL_TX_PIN
int "Serial PHY TX pin"
depends on GATEWAY_CHANNEL_COUNT >= 2 && (GATEWAY_CHANNEL2_PHY_UART1 || GATEWAY_CHANNEL2_PHY_UART2)
range 0 48
range -1 48
default 6
help
ESP32-S3 GPIO used by the channel 2 serial PHY transmit pin.
ESP32-S3 GPIO used by the channel 2 serial PHY transmit pin. Set to -1
to keep the UART driver's default TX routing.
config GATEWAY_CHANNEL2_SERIAL_RX_PIN
int "Serial PHY RX pin"
depends on GATEWAY_CHANNEL_COUNT >= 2 && (GATEWAY_CHANNEL2_PHY_UART1 || GATEWAY_CHANNEL2_PHY_UART2)
range 0 48
range -1 48
default 7
help
ESP32-S3 GPIO used by the channel 2 serial PHY receive pin.
ESP32-S3 GPIO used by the channel 2 serial PHY receive pin. Set to -1
to keep the UART driver's default RX routing.
config GATEWAY_CHANNEL2_SERIAL_BAUDRATE
int "Serial PHY baudrate"
@@ -542,8 +546,11 @@ config GATEWAY_MODBUS_UNIT_ID
config GATEWAY_MODBUS_SERIAL_UART_PORT
int "Default Modbus serial UART port"
depends on GATEWAY_MODBUS_BRIDGE_SUPPORTED && (GATEWAY_MODBUS_DEFAULT_TRANSPORT_RTU || GATEWAY_MODBUS_DEFAULT_TRANSPORT_ASCII)
range 0 2
range -1 2
default 1
help
UART used by the default Modbus serial server. Set to -1 to disable the
default serial UART runtime for this function.
config GATEWAY_MODBUS_ALLOW_UART0
bool "Allow Modbus/setup to claim UART0"
@@ -695,23 +702,60 @@ config GATEWAY_KNX_INDIVIDUAL_ADDRESS
Raw 16-bit individual address advertised to KNXnet/IP tunnel clients.
The default 4353 is 1.1.1.
config GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO
int "KNX programming button GPIO"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range -1 48
default -1
help
GPIO used to toggle KNX programming mode. Set to -1 to disable the local
KNX programming button.
config GATEWAY_KNX_PROGRAMMING_BUTTON_ACTIVE_LOW
bool "KNX programming button is active low"
depends on GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO >= 0
default y
config GATEWAY_KNX_PROGRAMMING_LED_GPIO
int "KNX programming LED GPIO"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range -1 48
default -1
help
GPIO used to show KNX programming mode. Set to -1 to disable the local
KNX programming LED.
config GATEWAY_KNX_PROGRAMMING_LED_ACTIVE_HIGH
bool "KNX programming LED is active high"
depends on GATEWAY_KNX_PROGRAMMING_LED_GPIO >= 0
default y
config GATEWAY_KNX_TP_UART_PORT
int "KNX TP UART port"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range 0 2
default 1
range -1 2
default -1
help
UART used by the KNX TP-UART interface. Set to -1 to disable TP-UART
while keeping KNXnet/IP tunnelling and routing available.
config GATEWAY_KNX_TP_TX_PIN
int "KNX TP UART TX pin"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range -1 48
default -1
help
GPIO used by the KNX TP-UART TX pin. Set to -1 to keep the UART driver's
default TX routing.
config GATEWAY_KNX_TP_RX_PIN
int "KNX TP UART RX pin"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range -1 48
default -1
help
GPIO used by the KNX TP-UART RX pin. Set to -1 to keep the UART driver's
default RX routing.
config GATEWAY_KNX_TP_BAUDRATE
int "KNX TP UART baudrate"
@@ -723,7 +767,7 @@ config GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE
int "KNX/IP bridge task stack bytes"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
range 6144 24576
default 8192
default 12288
config GATEWAY_BRIDGE_KNX_TASK_PRIORITY
int "KNX/IP bridge task priority"
@@ -916,23 +960,44 @@ config GATEWAY_STATUS_LED_ACTIVE_HIGH
default y
config GATEWAY_BOOT_BUTTON_GPIO
int "BOOT button GPIO"
int "Wi-Fi reset button GPIO"
range -1 48
default 0
default -1
help
GPIO used for Lua-compatible setup entry and Wi-Fi credential clearing. Set to -1 to disable.
GPIO used for long-press Wi-Fi credential clearing. Set to -1 to disable.
config GATEWAY_BOOT_BUTTON_ACTIVE_LOW
bool "BOOT button is active low"
bool "Wi-Fi reset button is active low"
depends on GATEWAY_BOOT_BUTTON_GPIO >= 0
default y
config GATEWAY_BOOT_BUTTON_LONG_PRESS_MS
int "BOOT button long press ms"
int "Wi-Fi reset button long press ms"
depends on GATEWAY_BOOT_BUTTON_GPIO >= 0
range 500 10000
default 3000
config GATEWAY_SETUP_AP_BUTTON_GPIO
int "Setup AP button GPIO"
range -1 48
default 0
help
GPIO used for entering setup AP mode. Set to -1 to disable local setup
AP entry by GPIO.
config GATEWAY_SETUP_AP_BUTTON_ACTIVE_LOW
bool "Setup AP button is active low"
depends on GATEWAY_SETUP_AP_BUTTON_GPIO >= 0
default y
config GATEWAY_BUTTON_TASK_STACK_SIZE
int "Gateway button task stack bytes"
depends on GATEWAY_BOOT_BUTTON_GPIO >= 0 || GATEWAY_SETUP_AP_BUTTON_GPIO >= 0
range 3072 12288
default 8192
help
Stack used by the GPIO button task and one-shot setup AP task.
endmenu
endmenu
+50 -6
View File
@@ -40,6 +40,21 @@
#define CONFIG_GATEWAY_BOOT_BUTTON_LONG_PRESS_MS 3000
#endif
#ifndef CONFIG_GATEWAY_SETUP_AP_BUTTON_GPIO
#ifdef CONFIG_GATEWAY_BOOT_BUTTON_GPIO
#define CONFIG_GATEWAY_SETUP_AP_BUTTON_GPIO CONFIG_GATEWAY_BOOT_BUTTON_GPIO
#ifdef CONFIG_GATEWAY_BOOT_BUTTON_ACTIVE_LOW
#define CONFIG_GATEWAY_SETUP_AP_BUTTON_ACTIVE_LOW 1
#endif
#else
#define CONFIG_GATEWAY_SETUP_AP_BUTTON_GPIO -1
#endif
#endif
#ifndef CONFIG_GATEWAY_BUTTON_TASK_STACK_SIZE
#define CONFIG_GATEWAY_BUTTON_TASK_STACK_SIZE 8192
#endif
#ifndef CONFIG_GATEWAY_USB_SETUP_CHANNEL_INDEX
#define CONFIG_GATEWAY_USB_SETUP_CHANNEL_INDEX 0
#endif
@@ -193,7 +208,7 @@
#endif
#ifndef CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE
#define CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE 8192
#define CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE 12288
#endif
#ifndef CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY
@@ -216,8 +231,16 @@
#define CONFIG_GATEWAY_KNX_INDIVIDUAL_ADDRESS 4353
#endif
#ifndef CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO
#define CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO -1
#endif
#ifndef CONFIG_GATEWAY_KNX_PROGRAMMING_LED_GPIO
#define CONFIG_GATEWAY_KNX_PROGRAMMING_LED_GPIO -1
#endif
#ifndef CONFIG_GATEWAY_KNX_TP_UART_PORT
#define CONFIG_GATEWAY_KNX_TP_UART_PORT 1
#define CONFIG_GATEWAY_KNX_TP_UART_PORT -1
#endif
#ifndef CONFIG_GATEWAY_KNX_TP_TX_PIN
@@ -579,21 +602,22 @@ bool ValidateChannelBindings() {
if (kKnxBridgeSupported) {
const int knx_uart = CONFIG_GATEWAY_KNX_TP_UART_PORT;
if (k485ControlEnabled && knx_uart == 0) {
if (knx_uart >= 0 && k485ControlEnabled && knx_uart == 0) {
ESP_LOGE(kTag, "KNX TP UART0 conflicts with the UART0 control bridge");
return false;
}
if (knx_uart == 0 && kConsoleOnUart0) {
if (knx_uart >= 0 && knx_uart == 0 && kConsoleOnUart0) {
ESP_LOGE(kTag, "KNX TP-UART on UART0 requires moving the ESP-IDF console off UART0");
return false;
}
if (kModbusBridgeSupported && kModbusDefaultSerialTransport &&
if (knx_uart >= 0 && kModbusBridgeSupported && kModbusDefaultSerialTransport &&
knx_uart == CONFIG_GATEWAY_MODBUS_SERIAL_UART_PORT) {
ESP_LOGE(kTag, "KNX TP UART%d conflicts with default Modbus serial UART", knx_uart);
return false;
}
for (int i = 0; i < CONFIG_GATEWAY_CHANNEL_COUNT; ++i) {
if (channels[i].enabled && channels[i].serial_phy && channels[i].uart_port == knx_uart) {
if (knx_uart >= 0 && channels[i].enabled && channels[i].serial_phy &&
channels[i].uart_port == knx_uart) {
ESP_LOGE(kTag, "KNX TP UART%d conflicts with DALI channel %d serial PHY", knx_uart,
i + 1);
return false;
@@ -840,6 +864,18 @@ extern "C" void app_main(void) {
default_knx.multicast_address = CONFIG_GATEWAY_KNX_MULTICAST_ADDRESS;
default_knx.individual_address =
static_cast<uint16_t>(CONFIG_GATEWAY_KNX_INDIVIDUAL_ADDRESS);
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
default_knx.programming_button_active_low = true;
#else
default_knx.programming_button_active_low = false;
#endif
#ifdef CONFIG_GATEWAY_KNX_PROGRAMMING_LED_ACTIVE_HIGH
default_knx.programming_led_active_high = true;
#else
default_knx.programming_led_active_high = false;
#endif
default_knx.tp_uart.uart_port = CONFIG_GATEWAY_KNX_TP_UART_PORT;
default_knx.tp_uart.tx_pin = CONFIG_GATEWAY_KNX_TP_TX_PIN;
default_knx.tp_uart.rx_pin = CONFIG_GATEWAY_KNX_TP_RX_PIN;
@@ -898,7 +934,10 @@ extern "C" void app_main(void) {
static_cast<uint32_t>(CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE);
network_config.status_led_gpio = CONFIG_GATEWAY_STATUS_LED_GPIO;
network_config.boot_button_gpio = CONFIG_GATEWAY_BOOT_BUTTON_GPIO;
network_config.setup_ap_button_gpio = CONFIG_GATEWAY_SETUP_AP_BUTTON_GPIO;
network_config.boot_button_long_press_ms = CONFIG_GATEWAY_BOOT_BUTTON_LONG_PRESS_MS;
network_config.boot_button_task_stack_size =
static_cast<uint32_t>(CONFIG_GATEWAY_BUTTON_TASK_STACK_SIZE);
#ifdef CONFIG_GATEWAY_STATUS_LED_ACTIVE_HIGH
network_config.status_led_active_high = true;
#else
@@ -908,6 +947,11 @@ extern "C" void app_main(void) {
network_config.boot_button_active_low = true;
#else
network_config.boot_button_active_low = false;
#endif
#ifdef CONFIG_GATEWAY_SETUP_AP_BUTTON_ACTIVE_LOW
network_config.setup_ap_button_active_low = true;
#else
network_config.setup_ap_button_active_low = false;
#endif
s_network = std::make_unique<gateway::GatewayNetworkService>(*s_controller, *s_runtime,
*s_dali_domain, network_config,
+15 -18
View File
@@ -596,7 +596,7 @@ CONFIG_PARTITION_TABLE_MD5=y
#
# Gateway App
#
CONFIG_GATEWAY_CHANNEL_COUNT=2
CONFIG_GATEWAY_CHANNEL_COUNT=1
#
# Gateway Channel 1
@@ -617,17 +617,6 @@ CONFIG_GATEWAY_CHANNEL1_SERIAL_QUERY_TIMEOUT_MS=100
#
# Gateway Channel 2
#
CONFIG_GATEWAY_CHANNEL2_GW_ID=4
# CONFIG_GATEWAY_CHANNEL2_PHY_DISABLED is not set
# CONFIG_GATEWAY_CHANNEL2_PHY_NATIVE is not set
# CONFIG_GATEWAY_CHANNEL2_PHY_UART1 is not set
CONFIG_GATEWAY_CHANNEL2_PHY_UART2=y
CONFIG_GATEWAY_CHANNEL2_SERIAL_TX_PIN=6
CONFIG_GATEWAY_CHANNEL2_SERIAL_RX_PIN=7
CONFIG_GATEWAY_CHANNEL2_SERIAL_BAUDRATE=9600
CONFIG_GATEWAY_CHANNEL2_SERIAL_RX_BUFFER=512
CONFIG_GATEWAY_CHANNEL2_SERIAL_TX_BUFFER=512
CONFIG_GATEWAY_CHANNEL2_SERIAL_QUERY_TIMEOUT_MS=100
# end of Gateway Channel 2
#
@@ -673,7 +662,7 @@ CONFIG_GATEWAY_ETHERNET_W5500_POLL_PERIOD_MS=0
CONFIG_GATEWAY_ETHERNET_W5500_CLOCK_MHZ=40
CONFIG_GATEWAY_ETHERNET_PHY_RESET_GPIO=-1
CONFIG_GATEWAY_ETHERNET_PHY_ADDR=1
CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE=3072
CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE=4096
# end of Gateway Wired Ethernet
CONFIG_GATEWAY_BRIDGE_SUPPORTED=y
@@ -698,11 +687,15 @@ CONFIG_GATEWAY_KNX_MULTICAST_ENABLED=y
CONFIG_GATEWAY_KNX_UDP_PORT=3671
CONFIG_GATEWAY_KNX_MULTICAST_ADDRESS="224.0.23.12"
CONFIG_GATEWAY_KNX_INDIVIDUAL_ADDRESS=4353
CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO=0
CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_ACTIVE_LOW=y
CONFIG_GATEWAY_KNX_PROGRAMMING_LED_GPIO=10
# CONFIG_GATEWAY_KNX_PROGRAMMING_LED_ACTIVE_HIGH is not set
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_BRIDGE_KNX_TASK_STACK_SIZE=8192
CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288
CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5
CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED=y
# CONFIG_GATEWAY_START_CLOUD_BRIDGE_ENABLED is not set
@@ -726,6 +719,8 @@ CONFIG_GATEWAY_STATUS_LED_GPIO=-1
CONFIG_GATEWAY_BOOT_BUTTON_GPIO=0
CONFIG_GATEWAY_BOOT_BUTTON_ACTIVE_LOW=y
CONFIG_GATEWAY_BOOT_BUTTON_LONG_PRESS_MS=3000
CONFIG_GATEWAY_SETUP_AP_BUTTON_GPIO=-1
CONFIG_GATEWAY_BUTTON_TASK_STACK_SIZE=8192
# end of Gateway Network Services
# end of Gateway App
@@ -2184,8 +2179,7 @@ CONFIG_LWIP_GARP_TMR_INTERVAL=60
CONFIG_LWIP_ESP_MLDV6_REPORT=y
CONFIG_LWIP_MLDV6_TMR_INTERVAL=40
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32
CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
# CONFIG_LWIP_DHCP_DOES_ACD_CHECK is not set
CONFIG_LWIP_DHCP_DOES_ACD_CHECK=y
# CONFIG_LWIP_DHCP_DOES_NOT_CHECK_OFFERED_IP is not set
# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set
CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y
@@ -2204,13 +2198,16 @@ CONFIG_LWIP_DHCPS_STATIC_ENTRIES=y
CONFIG_LWIP_DHCPS_ADD_DNS=y
# end of DHCP server
# CONFIG_LWIP_AUTOIP is not set
CONFIG_LWIP_AUTOIP=y
CONFIG_LWIP_AUTOIP_TRIES=2
CONFIG_LWIP_AUTOIP_MAX_CONFLICTS=9
CONFIG_LWIP_AUTOIP_RATE_LIMIT_INTERVAL=20
CONFIG_LWIP_IPV4=y
CONFIG_LWIP_IPV6=y
# CONFIG_LWIP_IPV6_AUTOCONFIG is not set
CONFIG_LWIP_IPV6_NUM_ADDRESSES=3
# CONFIG_LWIP_IPV6_FORWARD is not set
# CONFIG_LWIP_NETIF_STATUS_CALLBACK is not set
CONFIG_LWIP_NETIF_STATUS_CALLBACK=y
CONFIG_LWIP_NETIF_LOOPBACK=y
CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8
+271 -263
View File
@@ -663,17 +663,17 @@ CONFIG_GATEWAY_ETHERNET_IGNORE_INIT_FAILURE=y
#
# Gateway Wired Ethernet
#
CONFIG_GATEWAY_ETHERNET_W5500_SPI_HOST=1
CONFIG_GATEWAY_ETHERNET_W5500_SCLK_GPIO=14
CONFIG_GATEWAY_ETHERNET_W5500_MOSI_GPIO=13
CONFIG_GATEWAY_ETHERNET_W5500_MISO_GPIO=12
CONFIG_GATEWAY_ETHERNET_W5500_CS_GPIO=15
CONFIG_GATEWAY_ETHERNET_W5500_INT_GPIO=4
CONFIG_GATEWAY_ETHERNET_W5500_SPI_HOST=2
CONFIG_GATEWAY_ETHERNET_W5500_SCLK_GPIO=48
CONFIG_GATEWAY_ETHERNET_W5500_MOSI_GPIO=47
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=36
CONFIG_GATEWAY_ETHERNET_PHY_RESET_GPIO=5
CONFIG_GATEWAY_ETHERNET_W5500_CLOCK_MHZ=40
CONFIG_GATEWAY_ETHERNET_PHY_RESET_GPIO=-1
CONFIG_GATEWAY_ETHERNET_PHY_ADDR=1
CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE=3072
CONFIG_GATEWAY_ETHERNET_RX_TASK_STACK_SIZE=4096
# end of Gateway Wired Ethernet
CONFIG_GATEWAY_BRIDGE_SUPPORTED=y
@@ -698,6 +698,10 @@ CONFIG_GATEWAY_KNX_MULTICAST_ENABLED=y
CONFIG_GATEWAY_KNX_UDP_PORT=3671
CONFIG_GATEWAY_KNX_MULTICAST_ADDRESS="224.0.23.12"
CONFIG_GATEWAY_KNX_INDIVIDUAL_ADDRESS=4353
CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_GPIO=0
CONFIG_GATEWAY_KNX_PROGRAMMING_BUTTON_ACTIVE_LOW=y
CONFIG_GATEWAY_KNX_PROGRAMMING_LED_GPIO=10
# CONFIG_GATEWAY_KNX_PROGRAMMING_LED_ACTIVE_HIGH is not set
CONFIG_GATEWAY_KNX_TP_UART_PORT=0
CONFIG_GATEWAY_KNX_TP_TX_PIN=-1
CONFIG_GATEWAY_KNX_TP_RX_PIN=-1
@@ -726,6 +730,8 @@ CONFIG_GATEWAY_STATUS_LED_GPIO=-1
CONFIG_GATEWAY_BOOT_BUTTON_GPIO=0
CONFIG_GATEWAY_BOOT_BUTTON_ACTIVE_LOW=y
CONFIG_GATEWAY_BOOT_BUTTON_LONG_PRESS_MS=3000
CONFIG_GATEWAY_SETUP_AP_BUTTON_GPIO=-1
CONFIG_GATEWAY_BUTTON_TASK_STACK_SIZE=8192
# end of Gateway Network Services
# end of Gateway App
@@ -1075,12 +1081,12 @@ CONFIG_BT_CTRL_RX_ANTENNA_INDEX_EFF=0
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N0 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P3 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P6 is not set
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9=y
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P12 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P15 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P18 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P20 is not set
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=11
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P20=y
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=15
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100
CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20
@@ -1093,7 +1099,253 @@ CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE=100
CONFIG_BT_CTRL_DUPL_SCAN_CACHE_REFRESH_PERIOD=0
# CONFIG_BT_CTRL_BLE_MESH_SCAN_DUPL_EN is not set
# CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set
CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EFF=0
#
# MODEM SLEEP Options
#
# CONFIG_BT_CTRL_MODEM_SLEEP is not set
# end of MODEM SLEEP Options
CONFIG_BT_CTRL_SLEEP_MODE_EFF=0
CONFIG_BT_CTRL_SLEEP_CLOCK_EFF=0
CONFIG_BT_CTRL_HCI_TL_EFF=1
# CONFIG_BT_CTRL_AGC_RECORRECT_EN is not set
# CONFIG_BT_CTRL_SCAN_BACKOFF_UPPERLIMITMAX is not set
# CONFIG_BT_BLE_ADV_DATA_LENGTH_ZERO_AUX is not set
CONFIG_BT_CTRL_CHAN_ASS_EN=y
CONFIG_BT_CTRL_LE_PING_EN=y
#
# BLE disconnects when Instant Passed (0x28) occurs
#
# CONFIG_BT_CTRL_BLE_LLCP_CONN_UPDATE is not set
# CONFIG_BT_CTRL_BLE_LLCP_CHAN_MAP_UPDATE is not set
# CONFIG_BT_CTRL_BLE_LLCP_PHY_UPDATE is not set
# end of BLE disconnects when Instant Passed (0x28) occurs
# CONFIG_BT_CTRL_RUN_IN_FLASH_ONLY is not set
CONFIG_BT_CTRL_DTM_ENABLE=y
CONFIG_BT_CTRL_BLE_MASTER=y
# CONFIG_BT_CTRL_BLE_TEST is not set
CONFIG_BT_CTRL_BLE_SCAN=y
CONFIG_BT_CTRL_BLE_SECURITY_ENABLE=y
CONFIG_BT_CTRL_BLE_ADV=y
# CONFIG_BT_CTRL_CHECK_CONNECT_IND_ACCESS_ADDRESS is not set
#
# Controller debug log Options (Experimental)
#
# end of Controller debug log Options (Experimental)
# end of Controller Options
#
# Common Options
#
CONFIG_BT_ALARM_MAX_NUM=50
CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT=y
# CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS is not set
#
# BLE Log
#
# CONFIG_BLE_LOG_ENABLED is not set
# end of BLE Log
# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set
# CONFIG_BT_BLE_LOG_UHCI_OUT_ENABLED is not set
# CONFIG_BT_LE_USED_MEM_STATISTICS_ENABLED is not set
# end of Common Options
# CONFIG_BT_HCI_LOG_DEBUG_EN is not set
# end of Bluetooth
# CONFIG_BLE_MESH is not set
#
# Console Library
#
# CONFIG_CONSOLE_SORTED_HELP is not set
# end of Console Library
#
# Driver Configurations
#
#
# Legacy TWAI Driver Configurations
#
# CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set
CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
# end of Legacy TWAI Driver Configurations
#
# Legacy ADC Driver Configuration
#
# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set
#
# Legacy ADC Calibration Configuration
#
# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set
# end of Legacy ADC Calibration Configuration
# end of Legacy ADC Driver Configuration
#
# Legacy MCPWM Driver Configurations
#
# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy MCPWM Driver Configurations
#
# Legacy Timer Group Driver Configurations
#
# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Timer Group Driver Configurations
#
# Legacy RMT Driver Configurations
#
# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy RMT Driver Configurations
#
# Legacy I2S Driver Configurations
#
# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy I2S Driver Configurations
#
# Legacy I2C Driver Configurations
#
# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy I2C Driver Configurations
#
# Legacy PCNT Driver Configurations
#
# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy PCNT Driver Configurations
#
# Legacy SDM Driver Configurations
#
# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy SDM Driver Configurations
#
# Legacy Temperature Sensor Driver Configurations
#
# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Temperature Sensor Driver Configurations
#
# Legacy Touch Sensor Driver Configurations
#
# CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_TOUCH_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Touch Sensor Driver Configurations
# end of Driver Configurations
#
# eFuse Bit Manager
#
# CONFIG_EFUSE_CUSTOM_TABLE is not set
# CONFIG_EFUSE_VIRTUAL is not set
CONFIG_EFUSE_MAX_BLK_LEN=256
# end of eFuse Bit Manager
#
# ESP-TLS
#
CONFIG_ESP_TLS_USING_MBEDTLS=y
# CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set
CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y
# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set
# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set
# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set
# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set
# CONFIG_ESP_TLS_PSK_VERIFICATION is not set
# CONFIG_ESP_TLS_INSECURE is not set
CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y
# end of ESP-TLS
#
# ADC and ADC Calibration
#
# CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM is not set
# CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is not set
# CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3 is not set
# CONFIG_ADC_ENABLE_DEBUG_LOG is not set
# end of ADC and ADC Calibration
#
# Wireless Coexistence
#
CONFIG_ESP_COEX_ENABLED=y
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
# CONFIG_ESP_COEX_GPIO_DEBUG is not set
# end of Wireless Coexistence
#
# Common ESP-related
#
CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
# end of Common ESP-related
#
# ESP-Driver:Camera Controller Configurations
#
# CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE is not set
# end of ESP-Driver:Camera Controller Configurations
#
# ESP-Driver:GPIO Configurations
#
# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set
# 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_OBJ_CACHE_SAFE=y
# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:GPTimer Configurations
#
# ESP-Driver:I2C Configurations
#
# CONFIG_I2C_ISR_IRAM_SAFE is not set
# CONFIG_I2C_ENABLE_DEBUG_LOG is not set
# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set
CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y
# end of ESP-Driver:I2C Configurations
#
# ESP-Driver:I2S Configurations
#
# CONFIG_I2S_ISR_IRAM_SAFE is not set
# CONFIG_I2S_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:I2S Configurations
#
# ESP-Driver:LEDC Configurations
#
# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set
# end of ESP-Driver:LEDC Configurations
#
# ESP-Driver:MCPWM Configurations
@@ -1938,8 +2190,7 @@ CONFIG_LWIP_GARP_TMR_INTERVAL=60
CONFIG_LWIP_ESP_MLDV6_REPORT=y
CONFIG_LWIP_MLDV6_TMR_INTERVAL=40
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32
CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
# CONFIG_LWIP_DHCP_DOES_ACD_CHECK is not set
CONFIG_LWIP_DHCP_DOES_ACD_CHECK=y
# CONFIG_LWIP_DHCP_DOES_NOT_CHECK_OFFERED_IP is not set
# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set
CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y
@@ -1958,13 +2209,16 @@ CONFIG_LWIP_DHCPS_STATIC_ENTRIES=y
CONFIG_LWIP_DHCPS_ADD_DNS=y
# end of DHCP server
# CONFIG_LWIP_AUTOIP is not set
CONFIG_LWIP_AUTOIP=y
CONFIG_LWIP_AUTOIP_TRIES=2
CONFIG_LWIP_AUTOIP_MAX_CONFLICTS=9
CONFIG_LWIP_AUTOIP_RATE_LIMIT_INTERVAL=20
CONFIG_LWIP_IPV4=y
CONFIG_LWIP_IPV6=y
# CONFIG_LWIP_IPV6_AUTOCONFIG is not set
CONFIG_LWIP_IPV6_NUM_ADDRESSES=3
# CONFIG_LWIP_IPV6_FORWARD is not set
# CONFIG_LWIP_NETIF_STATUS_CALLBACK is not set
CONFIG_LWIP_NETIF_STATUS_CALLBACK=y
CONFIG_LWIP_NETIF_LOOPBACK=y
CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8
@@ -2565,249 +2819,3 @@ CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y
# end of Component config
# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set
# Deprecated options for backward compatibility
# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set
# CONFIG_NO_BLOBS is not set
# CONFIG_APP_ROLLBACK_ENABLE is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set
CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set
CONFIG_LOG_BOOTLOADER_LEVEL=3
# CONFIG_FLASH_ENCRYPTION_ENABLED is not set
# CONFIG_FLASHMODE_QIO is not set
# CONFIG_FLASHMODE_QOUT is not set
CONFIG_FLASHMODE_DIO=y
# CONFIG_FLASHMODE_DOUT is not set
CONFIG_MONITOR_BAUD=115200
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2
# CONFIG_CXX_EXCEPTIONS is not set
CONFIG_STACK_CHECK_NONE=y
# CONFIG_STACK_CHECK_NORM is not set
# CONFIG_STACK_CHECK_STRONG is not set
# CONFIG_STACK_CHECK_ALL is not set
# CONFIG_WARN_WRITE_STRINGS is not set
# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set
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_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_TASK_STACK_SIZE=4096
CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096
CONFIG_NIMBLE_ROLE_CENTRAL=y
CONFIG_NIMBLE_ROLE_PERIPHERAL=y
CONFIG_NIMBLE_ROLE_BROADCASTER=y
CONFIG_NIMBLE_ROLE_OBSERVER=y
CONFIG_NIMBLE_SM_LEGACY=y
CONFIG_NIMBLE_SM_SC=y
# CONFIG_NIMBLE_SM_SC_DEBUG_KEYS is not set
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_CCCDS=8
CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y
# CONFIG_NIMBLE_HS_FLOW_CTRL is not set
CONFIG_NIMBLE_ATT_PREFERRED_MTU=256
CONFIG_NIMBLE_L2CAP_COC_MAX_NUM=0
CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=12
CONFIG_BT_NIMBLE_ACL_BUF_COUNT=24
CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255
CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70
CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=30
CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=8
CONFIG_NIMBLE_SVC_GAP_DEVICE_NAME="nimble"
CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31
CONFIG_NIMBLE_SVC_GAP_APPEARANCE=0
# CONFIG_NIMBLE_MESH is not set
# CONFIG_NIMBLE_DEBUG is not set
# CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
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_MCPWM_ISR_IRAM_SAFE is not set
# CONFIG_EVENT_LOOP_PROFILING is not set
CONFIG_POST_EVENTS_FROM_ISR=y
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
CONFIG_GDBSTUB_SUPPORT_TASKS=y
CONFIG_GDBSTUB_MAX_TASKS=32
# CONFIG_OTA_ALLOW_HTTP is not set
CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000
CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000
CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y
# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS is not set
# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set
# CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set
CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024
CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y
CONFIG_BROWNOUT_DET=y
CONFIG_ESP32S3_BROWNOUT_DET=y
CONFIG_BROWNOUT_DET_LVL_SEL_7=y
CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y
# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_1 is not set
CONFIG_BROWNOUT_DET_LVL=7
CONFIG_ESP32S3_BROWNOUT_DET_LVL=7
CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y
CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y
# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set
CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
CONFIG_ESP32_PHY_MAX_TX_POWER=20
# CONFIG_REDUCE_PHY_TX_POWER is not set
# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set
CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y
CONFIG_PM_POWER_DOWN_TAGMEM_IN_LIGHT_SLEEP=y
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_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
CONFIG_MAIN_TASK_STACK_SIZE=3584
# CONFIG_CONSOLE_UART_DEFAULT is not set
# CONFIG_CONSOLE_UART_CUSTOM is not set
# CONFIG_CONSOLE_UART_NONE is not set
# CONFIG_ESP_CONSOLE_UART_NONE is not set
CONFIG_CONSOLE_UART_NUM=-1
CONFIG_INT_WDT=y
CONFIG_INT_WDT_TIMEOUT_MS=300
CONFIG_INT_WDT_CHECK_CPU1=y
CONFIG_TASK_WDT=y
CONFIG_ESP_TASK_WDT=y
# CONFIG_TASK_WDT_PANIC is not set
CONFIG_TASK_WDT_TIMEOUT_S=5
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set
CONFIG_ESP32S3_DEBUG_OCDAWARE=y
CONFIG_IPC_TASK_STACK_SIZE=1280
CONFIG_TIMER_TASK_STACK_SIZE=3584
CONFIG_ESP32_WIFI_ENABLED=y
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32
# CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y
CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32
# CONFIG_ESP32_WIFI_CSI_ENABLED is not set
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
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_SOFTAP_BEACON_MAX_LEN=752
CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32
CONFIG_ESP32_WIFI_IRAM_OPT=y
CONFIG_ESP32_WIFI_RX_IRAM_OPT=y
CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y
CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA=y
CONFIG_WPA_MBEDTLS_CRYPTO=y
CONFIG_WPA_MBEDTLS_TLS_CLIENT=y
# CONFIG_WPA_WAPI_PSK is not set
# CONFIG_WPA_SUITE_B_192 is not set
# CONFIG_WPA_11KV_SUPPORT is not set
# CONFIG_WPA_MBO_SUPPORT is not set
# CONFIG_WPA_DPP_SUPPORT is not set
# CONFIG_WPA_11R_SUPPORT is not set
# CONFIG_WPA_WPS_SOFTAP_REGISTRAR is not set
# CONFIG_WPA_WPS_STRICT is not set
# CONFIG_WPA_DEBUG_PRINT is not set
# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set
# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set
CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y
CONFIG_TIMER_TASK_PRIORITY=1
CONFIG_TIMER_TASK_STACK_DEPTH=2048
CONFIG_TIMER_QUEUE_LENGTH=10
# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set
CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y
# CONFIG_HAL_ASSERTION_SILIENT is not set
# CONFIG_L2_TO_L3_COPY is not set
CONFIG_ESP_GRATUITOUS_ARP=y
CONFIG_GARP_TMR_INTERVAL=60
CONFIG_TCPIP_RECVMBOX_SIZE=32
CONFIG_TCP_MAXRTX=12
CONFIG_TCP_SYNMAXRTX=12
CONFIG_TCP_MSS=1440
CONFIG_TCP_MSL=60000
CONFIG_TCP_SND_BUF_DEFAULT=5760
CONFIG_TCP_WND_DEFAULT=5760
CONFIG_TCP_RECVMBOX_SIZE=6
CONFIG_TCP_QUEUE_OOSEQ=y
CONFIG_TCP_OVERSIZE_MSS=y
# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set
# CONFIG_TCP_OVERSIZE_DISABLE is not set
CONFIG_UDP_RECVMBOX_SIZE=6
CONFIG_TCPIP_TASK_STACK_SIZE=3072
CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set
# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set
CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF
# CONFIG_PPP_SUPPORT is not set
CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y
# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set
# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set
# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set
# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set
CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y
# CONFIG_NEWLIB_NANO_FORMAT is not set
CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y
CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER=y
CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1=y
# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC is not set
# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_SYSTIMER is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 is not set
# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_NONE is not set
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5
CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
CONFIG_ESP32_PTHREAD_STACK_MIN=768
CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y
# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set
# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set
CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1
CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread"
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y
# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set
# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set
CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y
CONFIG_SUPPORT_TERMIOS=y
CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1
# End of deprecated options
+3 -2
View File
@@ -290,8 +290,9 @@ esp_err_t DaliDomainService::bindSerialBus(const DaliSerialBusConfig& config) {
ESP_LOGE(kTag, "failed to configure uart%d: %s", config.uart_port, esp_err_to_name(err));
return err;
}
err = uart_set_pin(uart, config.tx_pin, config.rx_pin, UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE);
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,
config.tx_pin, config.rx_pin, esp_err_to_name(err));
@@ -1,5 +1,6 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
@@ -37,7 +38,7 @@ struct GatewayBridgeServiceConfig {
std::vector<int> reserved_uart_ports;
uint32_t bacnet_task_stack_size{8192};
UBaseType_t bacnet_task_priority{5};
uint32_t knx_task_stack_size{8192};
uint32_t knx_task_stack_size{12288};
UBaseType_t knx_task_priority{5};
std::optional<GatewayKnxConfig> default_knx_config;
};
@@ -66,6 +67,14 @@ class GatewayBridgeService {
ChannelRuntime* findRuntime(uint8_t gateway_id);
const ChannelRuntime* findRuntime(uint8_t gateway_id) const;
ChannelRuntime* selectKnxEndpointRuntime();
bool isKnxEndpointRuntime(const ChannelRuntime* runtime) const;
esp_err_t startKnxEndpoint(ChannelRuntime* requested_runtime,
std::set<int>* used_uarts = nullptr);
esp_err_t stopKnxEndpoint(ChannelRuntime* requested_runtime);
DaliBridgeResult routeKnxCemiFrame(const uint8_t* data, size_t len);
DaliBridgeResult routeKnxGroupWrite(uint16_t group_address, const uint8_t* data,
size_t len);
void handleDaliRawFrame(const DaliRawFrame& frame);
void collectUsedRuntimeResources(uint8_t except_gateway_id,
std::set<uint16_t>* modbus_tcp_ports,
@@ -76,6 +85,7 @@ class GatewayBridgeService {
GatewayCache& cache_;
GatewayBridgeServiceConfig config_;
std::vector<std::unique_ptr<ChannelRuntime>> runtimes_;
ChannelRuntime* knx_endpoint_runtime_{nullptr};
};
} // namespace gateway
+275 -93
View File
@@ -15,7 +15,6 @@
#include "gateway_knx.hpp"
#include "gateway_modbus.hpp"
#include "gateway_provisioning.hpp"
#include "openknx_idf/ets_memory_loader.h"
#include "openknx_idf/security_storage.h"
#include "cJSON.h"
@@ -1296,12 +1295,20 @@ uart_stop_bits_t UartStopBits(int bits) {
} // namespace
struct GatewayBridgeService::ChannelRuntime {
explicit ChannelRuntime(DaliDomainService& domain, GatewayCache& cache, DaliChannelInfo channel,
explicit ChannelRuntime(GatewayBridgeService& service, DaliDomainService& domain,
GatewayCache& cache, DaliChannelInfo channel,
GatewayBridgeServiceConfig service_config)
: domain(domain), cache(cache), channel(std::move(channel)), service_config(service_config),
: service(service),
domain(domain),
cache(cache),
channel(std::move(channel)),
service_config(service_config),
lock(xSemaphoreCreateRecursiveMutex()) {}
~ChannelRuntime() {
if (knx_router != nullptr) {
knx_router->stop();
}
if (cloud != nullptr) {
cloud->stop();
}
@@ -1311,6 +1318,7 @@ struct GatewayBridgeService::ChannelRuntime {
}
}
GatewayBridgeService& service;
DaliDomainService& domain;
GatewayCache& cache;
DaliChannelInfo channel;
@@ -1427,18 +1435,17 @@ struct GatewayBridgeService::ChannelRuntime {
knx = std::make_unique<GatewayKnxBridge>(*engine);
knx_router = std::make_unique<GatewayKnxTpIpRouter>(
*knx, [this](const uint8_t* data, size_t len) {
LockGuard guard(lock);
if (knx == nullptr) {
DaliBridgeResult result;
result.error = "KNX bridge is not ready";
return result;
}
return knx->handleCemiFrame(data, len);
return service.routeKnxCemiFrame(data, len);
},
openKnxNamespace());
if (knx_config.has_value()) {
knx->setConfig(knx_config.value());
knx_router->setConfig(knx_config.value());
knx_router->setGroupWriteHandler(
[this](uint16_t group_address, const uint8_t* data, size_t len) {
return service.routeKnxGroupWrite(group_address, data, len);
});
if (const auto active_knx = activeKnxConfigLocked(); active_knx.has_value()) {
knx->setConfig(active_knx.value());
knx_router->setConfig(active_knx.value());
knx_router->setCommissioningOnly(!knx_config.has_value());
}
#if defined(CONFIG_GATEWAY_BACNET_BRIDGE_SUPPORTED)
@@ -1459,33 +1466,8 @@ struct GatewayBridgeService::ChannelRuntime {
}
void refreshOpenKnxEtsAssociationsLocked() {
if (!service_config.knx_enabled) {
return;
}
const auto active_config = activeKnxConfigLocked();
if (!active_config.has_value()) {
return;
}
const auto snapshot = openknx::LoadEtsMemorySnapshot(openKnxNamespace());
const bool has_downloaded_address = snapshot.individual_address != 0 &&
snapshot.individual_address != 0xffff;
if (!snapshot.configured && !has_downloaded_address && snapshot.associations.empty()) {
return;
}
GatewayKnxConfig updated = active_config.value();
if (has_downloaded_address) {
updated.individual_address = snapshot.individual_address;
}
updated.ets_associations.clear();
updated.ets_associations.reserve(snapshot.associations.size());
for (const auto& association : snapshot.associations) {
updated.ets_associations.push_back(GatewayKnxEtsAssociation{
association.group_address, association.group_object_number});
}
knx_config = std::move(updated);
ESP_LOGI(kTag, "gateway=%u loaded OpenKNX ETS address=0x%04x associations=%u from NVS namespace %s",
channel.gateway_id, snapshot.individual_address,
static_cast<unsigned>(snapshot.associations.size()), openKnxNamespace().c_str());
// Bau07B0/OpenKNX memory restore is stack-heavy and owns TP-UART internals;
// the live KNX task restores and syncs ETS associations after startup.
}
std::optional<DaliDomainSnapshot> diagnosticSnapshotLocked(int short_address,
@@ -2056,25 +2038,41 @@ struct GatewayBridgeService::ChannelRuntime {
knx_last_error = validation_error;
return validation_err;
}
if (config->ip_router_enabled && used_ports != nullptr) {
if (used_ports->find(config->udp_port) != used_ports->end()) {
knx_last_error = "duplicate KNXnet/IP UDP port " + std::to_string(config->udp_port);
GatewayKnxConfig runtime_config = config.value();
if (runtime_config.ip_router_enabled && used_ports != nullptr) {
if (used_ports->find(runtime_config.udp_port) != used_ports->end()) {
knx_last_error = "KNXnet/IP UDP port " + std::to_string(runtime_config.udp_port) +
" is already owned by another runtime";
ESP_LOGW(kTag, "gateway=%u skips duplicate KNXnet/IP UDP port %u",
channel.gateway_id, static_cast<unsigned>(runtime_config.udp_port));
return ESP_ERR_INVALID_STATE;
}
used_ports->insert(config->udp_port);
used_ports->insert(runtime_config.udp_port);
}
if (config->ip_router_enabled && used_uarts != nullptr) {
const int uart_port = config->tp_uart.uart_port;
if (GatewayKnxConfigUsesTpUart(runtime_config) && used_uarts != nullptr) {
const int uart_port = runtime_config.tp_uart.uart_port;
if (used_uarts->find(uart_port) != used_uarts->end()) {
knx_last_error = "KNX TP-UART UART" + std::to_string(uart_port) +
" is already used by another runtime";
return ESP_ERR_INVALID_STATE;
ESP_LOGW(kTag,
"gateway=%u KNX TP-UART UART%d is already owned by another runtime; "
"starting this KNX/IP runtime without opening a second UART driver",
channel.gateway_id, uart_port);
runtime_config.tp_uart.uart_port = -1;
}
used_uarts->insert(uart_port);
}
knx->setConfig(config.value());
knx_router->setConfig(config.value());
if (!config->ip_router_enabled) {
const bool commissioning_only = !knx_config.has_value();
ESP_LOGI(kTag,
"gateway=%u KNX/IP start config namespace=%s storedConfig=%d udp=%u tunnel=%d "
"multicast=%d multicastGroup=%s mainGroup=%u tpUart=%d tx=%d rx=%d individual=0x%04x",
channel.gateway_id, openKnxNamespace().c_str(), !commissioning_only,
static_cast<unsigned>(runtime_config.udp_port), runtime_config.tunnel_enabled,
runtime_config.multicast_enabled, runtime_config.multicast_address.c_str(),
static_cast<unsigned>(runtime_config.main_group), runtime_config.tp_uart.uart_port,
runtime_config.tp_uart.tx_pin, runtime_config.tp_uart.rx_pin,
runtime_config.individual_address);
knx->setConfig(runtime_config);
knx_router->setConfig(runtime_config);
knx_router->setCommissioningOnly(commissioning_only);
if (!runtime_config.ip_router_enabled) {
knx_started = false;
return ESP_ERR_NOT_SUPPORTED;
}
@@ -2084,8 +2082,18 @@ struct GatewayBridgeService::ChannelRuntime {
knx_started = err == ESP_OK;
if (err != ESP_OK) {
knx_last_error = knx_router->lastError().empty()
? "failed to start KNX TP-UART router"
? "failed to start KNXnet/IP router"
: knx_router->lastError();
ESP_LOGW(kTag, "gateway=%u KNX/IP start failed err=%s(%d) detail=%s",
channel.gateway_id, esp_err_to_name(err), static_cast<int>(err),
knx_last_error.c_str());
} else {
if (knx_router->tpUartOnline() && used_uarts != nullptr) {
used_uarts->insert(runtime_config.tp_uart.uart_port);
}
ESP_LOGI(kTag, "gateway=%u KNX/IP started namespace=%s udp=%u detail=%s",
channel.gateway_id, openKnxNamespace().c_str(),
static_cast<unsigned>(runtime_config.udp_port), knx_router->lastError().c_str());
}
return err;
}
@@ -3014,12 +3022,15 @@ struct GatewayBridgeService::ChannelRuntime {
const std::optional<GatewayKnxConfig>& candidate_knx,
std::string* error_message = nullptr) const {
const int uart_port = config.serial.uart_port;
if (uart_port < 0 || uart_port > 2) {
if (uart_port < -1 || uart_port > 2) {
if (error_message != nullptr) {
*error_message = "Modbus serial UART port must be 0, 1, or 2";
*error_message = "Modbus serial UART port must be -1, 0, 1, or 2";
}
return ESP_ERR_INVALID_ARG;
}
if (uart_port < 0) {
return ESP_OK;
}
if (uart_port == 0 && !service_config.allow_modbus_uart0) {
if (error_message != nullptr) {
*error_message =
@@ -3035,7 +3046,8 @@ struct GatewayBridgeService::ChannelRuntime {
return ESP_ERR_INVALID_STATE;
}
if (service_config.knx_enabled && candidate_knx.has_value() &&
candidate_knx->ip_router_enabled && candidate_knx->tp_uart.uart_port == uart_port) {
GatewayKnxConfigUsesTpUart(candidate_knx.value()) &&
candidate_knx->tp_uart.uart_port == uart_port) {
if (error_message != nullptr) {
*error_message = "Modbus serial UART" + std::to_string(uart_port) +
" conflicts with KNX TP-UART; choose another free UART for RS485";
@@ -3048,16 +3060,16 @@ struct GatewayBridgeService::ChannelRuntime {
esp_err_t validateKnxConfigLocked(const GatewayKnxConfig& config,
const std::optional<GatewayModbusConfig>& candidate_modbus,
std::string* error_message = nullptr) const {
if (!config.ip_router_enabled) {
return ESP_OK;
}
const int uart_port = config.tp_uart.uart_port;
if (uart_port < 0 || uart_port > 2) {
if (config.tp_uart.uart_port < -1 || config.tp_uart.uart_port > 2) {
if (error_message != nullptr) {
*error_message = "KNX TP-UART port must be 0, 1, or 2";
*error_message = "KNX TP-UART port must be -1, 0, 1, or 2";
}
return ESP_ERR_INVALID_ARG;
}
if (!config.ip_router_enabled || !GatewayKnxConfigUsesTpUart(config)) {
return ESP_OK;
}
const int uart_port = config.tp_uart.uart_port;
if (uart_port == 0 && !service_config.allow_knx_uart0) {
if (error_message != nullptr) {
*error_message =
@@ -3144,7 +3156,18 @@ struct GatewayBridgeService::ChannelRuntime {
if (knx_config.has_value()) {
return knx_config;
}
return service_config.default_knx_config;
if (!service_config.default_knx_config.has_value()) {
return std::nullopt;
}
GatewayKnxConfig config = service_config.default_knx_config.value();
const uint8_t channel_index = channel.channel_index;
config.main_group = static_cast<uint8_t>(std::min<int>(31, config.main_group + channel_index));
const uint16_t device = config.individual_address & 0x00ff;
if (device > 0 && device + channel_index <= 0x00ff) {
config.individual_address = static_cast<uint16_t>((config.individual_address & 0xff00) |
(device + channel_index));
}
return config;
}
esp_err_t saveKnxConfig(const GatewayKnxConfig& config,
@@ -3165,12 +3188,7 @@ struct GatewayBridgeService::ChannelRuntime {
return validation_err;
}
const bool restart_router = knx_started || (knx_router != nullptr && knx_router->started());
if (restart_router && merged_config.ip_router_enabled && used_ports != nullptr &&
used_ports->find(merged_config.udp_port) != used_ports->end()) {
knx_last_error = "duplicate KNXnet/IP UDP port " + std::to_string(merged_config.udp_port);
return ESP_ERR_INVALID_STATE;
}
if (restart_router && merged_config.ip_router_enabled && used_uarts != nullptr &&
if (restart_router && GatewayKnxConfigUsesTpUart(merged_config) && used_uarts != nullptr &&
used_uarts->find(merged_config.tp_uart.uart_port) != used_uarts->end()) {
knx_last_error = "KNX TP-UART UART" + std::to_string(merged_config.tp_uart.uart_port) +
" is already used by another runtime";
@@ -3195,6 +3213,7 @@ struct GatewayBridgeService::ChannelRuntime {
}
if (knx_router != nullptr) {
knx_router->setConfig(merged_config);
knx_router->setCommissioningOnly(false);
}
if (restart_router) {
return startKnx(used_ports, used_uarts);
@@ -3350,6 +3369,10 @@ struct GatewayBridgeService::ChannelRuntime {
return ESP_ERR_NOT_FOUND;
}
if (GatewayModbusTransportIsSerial(config->transport)) {
if (config->serial.uart_port < 0) {
modbus_last_error = "Modbus serial UART disabled";
return ESP_ERR_NOT_SUPPORTED;
}
std::string validation_error;
const esp_err_t serial_err = validateSerialModbusConfigLocked(
config.value(), activeKnxConfigLocked(), &validation_error);
@@ -3532,8 +3555,10 @@ struct GatewayBridgeService::ChannelRuntime {
}
const int rts_pin = config.serial.rs485.enabled ? config.serial.rs485.de_pin
: UART_PIN_NO_CHANGE;
err = uart_set_pin(uart_port, config.serial.tx_pin, config.serial.rx_pin, rts_pin,
UART_PIN_NO_CHANGE);
err = uart_set_pin(uart_port,
config.serial.tx_pin < 0 ? UART_PIN_NO_CHANGE : config.serial.tx_pin,
config.serial.rx_pin < 0 ? UART_PIN_NO_CHANGE : config.serial.rx_pin,
rts_pin < 0 ? UART_PIN_NO_CHANGE : rts_pin, UART_PIN_NO_CHANGE);
if (err != ESP_OK) {
return err;
}
@@ -3794,7 +3819,7 @@ esp_err_t GatewayBridgeService::start() {
const auto channels = dali_domain_.channelInfo();
runtimes_.reserve(channels.size());
for (const auto& channel : channels) {
auto runtime = std::make_unique<ChannelRuntime>(dali_domain_, cache_, channel, config_);
auto runtime = std::make_unique<ChannelRuntime>(*this, dali_domain_, cache_, channel, config_);
const esp_err_t err = runtime->start();
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to start bridge runtime gateway=%u: %s", channel.gateway_id,
@@ -3820,13 +3845,18 @@ esp_err_t GatewayBridgeService::start() {
}
if (config_.knx_enabled && config_.knx_startup_enabled) {
std::set<uint16_t> used_knx_ports;
for (const auto& runtime : runtimes_) {
const esp_err_t err = runtime->startKnx(&used_knx_ports, &used_serial_uarts);
if (err != ESP_OK && err != ESP_ERR_NOT_FOUND && err != ESP_ERR_NOT_SUPPORTED) {
ESP_LOGW(kTag, "gateway=%u KNX/IP startup skipped: %s", runtime->channel.gateway_id,
esp_err_to_name(err));
}
ChannelRuntime* owner = selectKnxEndpointRuntime();
const esp_err_t err = startKnxEndpoint(owner, &used_serial_uarts);
if (err != ESP_OK && err != ESP_ERR_NOT_FOUND && err != ESP_ERR_NOT_SUPPORTED) {
const char* detail = owner == nullptr
? "no KNX endpoint owner"
: (!owner->knx_last_error.empty()
? owner->knx_last_error.c_str()
: (owner->knx_router == nullptr
? "router unavailable"
: owner->knx_router->lastError().c_str()));
ESP_LOGW(kTag, "KNX/IP startup skipped: %s(%d) detail=%s",
esp_err_to_name(err), static_cast<int>(err), detail);
}
}
@@ -3863,20 +3893,170 @@ const GatewayBridgeService::ChannelRuntime* GatewayBridgeService::findRuntime(
return nullptr;
}
GatewayBridgeService::ChannelRuntime* GatewayBridgeService::selectKnxEndpointRuntime() {
auto eligible = [](ChannelRuntime* runtime) {
if (runtime == nullptr) {
return false;
}
LockGuard guard(runtime->lock);
const auto config = runtime->activeKnxConfigLocked();
return config.has_value() && config->ip_router_enabled;
};
if (eligible(knx_endpoint_runtime_)) {
return knx_endpoint_runtime_;
}
ChannelRuntime* selected = nullptr;
for (const auto& runtime : runtimes_) {
if (!eligible(runtime.get())) {
continue;
}
if (selected == nullptr || runtime->channel.channel_index < selected->channel.channel_index) {
selected = runtime.get();
}
}
knx_endpoint_runtime_ = selected;
if (selected != nullptr) {
ESP_LOGI(kTag, "gateway=%u owns shared KNXnet/IP endpoint", selected->channel.gateway_id);
}
return selected;
}
bool GatewayBridgeService::isKnxEndpointRuntime(const ChannelRuntime* runtime) const {
return runtime != nullptr && runtime == knx_endpoint_runtime_;
}
esp_err_t GatewayBridgeService::startKnxEndpoint(ChannelRuntime* requested_runtime,
std::set<int>* used_uarts) {
if (!config_.knx_enabled) {
return ESP_ERR_NOT_SUPPORTED;
}
ChannelRuntime* owner = selectKnxEndpointRuntime();
if (owner == nullptr) {
return ESP_ERR_NOT_FOUND;
}
if (requested_runtime != nullptr && requested_runtime != owner) {
ESP_LOGI(kTag, "gateway=%u requested KNX start; shared endpoint remains owned by gateway=%u",
requested_runtime->channel.gateway_id, owner->channel.gateway_id);
}
if (used_uarts != nullptr) {
LockGuard guard(owner->lock);
const auto owner_config = owner->activeKnxConfigLocked();
if (owner_config.has_value() && GatewayKnxConfigUsesTpUart(owner_config.value())) {
used_uarts->erase(owner_config->tp_uart.uart_port);
}
}
std::set<uint16_t> used_knx_ports;
for (const auto& runtime : runtimes_) {
if (runtime.get() == owner) {
continue;
}
LockGuard guard(runtime->lock);
if (runtime->knx_started ||
(runtime->knx_router != nullptr && runtime->knx_router->started())) {
const auto config = runtime->activeKnxConfigLocked();
if (config.has_value()) {
used_knx_ports.insert(config->udp_port);
}
}
}
return owner->startKnx(&used_knx_ports, used_uarts);
}
esp_err_t GatewayBridgeService::stopKnxEndpoint(ChannelRuntime* requested_runtime) {
ChannelRuntime* owner = knx_endpoint_runtime_ != nullptr ? knx_endpoint_runtime_
: selectKnxEndpointRuntime();
if (owner == nullptr) {
return ESP_ERR_NOT_FOUND;
}
if (requested_runtime != nullptr && requested_runtime != owner) {
ESP_LOGI(kTag, "gateway=%u requested KNX stop; stopping shared endpoint owned by gateway=%u",
requested_runtime->channel.gateway_id, owner->channel.gateway_id);
}
return owner->stopKnx();
}
DaliBridgeResult GatewayBridgeService::routeKnxCemiFrame(const uint8_t* data, size_t len) {
std::vector<ChannelRuntime*> matches;
for (const auto& runtime : runtimes_) {
LockGuard guard(runtime->lock);
if (runtime->knx != nullptr && runtime->knx->matchesCemiFrame(data, len)) {
matches.push_back(runtime.get());
}
}
if (matches.empty()) {
DaliBridgeResult result;
result.error = "No DALI bridge mapping matched KNX cEMI group write";
return result;
}
if (matches.size() > 1) {
DaliBridgeResult result;
result.error = "KNX cEMI group write matched multiple DALI bridge channels";
ESP_LOGW(kTag, "%s", result.error.c_str());
return result;
}
ChannelRuntime* runtime = matches.front();
LockGuard guard(runtime->lock);
if (runtime->knx == nullptr || !runtime->knx->matchesCemiFrame(data, len)) {
DaliBridgeResult result;
result.error = "DALI bridge mapping changed before KNX cEMI dispatch";
return result;
}
return runtime->knx->handleCemiFrame(data, len);
}
DaliBridgeResult GatewayBridgeService::routeKnxGroupWrite(uint16_t group_address,
const uint8_t* data, size_t len) {
std::vector<ChannelRuntime*> matches;
for (const auto& runtime : runtimes_) {
LockGuard guard(runtime->lock);
if (runtime->knx != nullptr && runtime->knx->matchesGroupAddress(group_address)) {
matches.push_back(runtime.get());
}
}
if (matches.empty()) {
DaliBridgeResult result;
result.error = "No DALI bridge mapping matched KNX group " +
GatewayKnxGroupAddressString(group_address);
return result;
}
if (matches.size() > 1) {
DaliBridgeResult result;
result.error = "KNX group " + GatewayKnxGroupAddressString(group_address) +
" matched multiple DALI bridge channels";
ESP_LOGW(kTag, "%s", result.error.c_str());
return result;
}
ChannelRuntime* runtime = matches.front();
LockGuard guard(runtime->lock);
if (runtime->knx == nullptr || !runtime->knx->matchesGroupAddress(group_address)) {
DaliBridgeResult result;
result.error = "DALI bridge mapping changed before KNX group dispatch";
return result;
}
return runtime->knx->handleGroupWrite(group_address, data, len);
}
void GatewayBridgeService::handleDaliRawFrame(const DaliRawFrame& frame) {
const auto update = DecodeDaliKnxStatusUpdate(frame);
if (!update.has_value()) {
return;
}
auto* runtime = findRuntime(frame.gateway_id);
if (runtime == nullptr) {
auto* owner = knx_endpoint_runtime_ != nullptr ? knx_endpoint_runtime_
: selectKnxEndpointRuntime();
if (owner == nullptr || owner->channel.gateway_id != frame.gateway_id) {
return;
}
LockGuard guard(runtime->lock);
if (!runtime->knx_started || runtime->knx_router == nullptr) {
LockGuard guard(owner->lock);
if (!owner->knx_started || owner->knx_router == nullptr) {
return;
}
runtime->knx_router->publishDaliStatus(update->target, update->actual_level);
owner->knx_router->publishDaliStatus(update->target, update->actual_level);
}
void GatewayBridgeService::collectUsedRuntimeResources(
@@ -3909,7 +4089,8 @@ void GatewayBridgeService::collectUsedRuntimeResources(
if (knx_udp_ports != nullptr) {
knx_udp_ports->insert(knx_config->udp_port);
}
if (serial_uarts != nullptr) {
if (serial_uarts != nullptr && runtime->knx_router != nullptr &&
runtime->knx_router->tpUartOnline()) {
serial_uarts->insert(knx_config->tp_uart.uart_port);
}
}
@@ -4214,20 +4395,21 @@ GatewayBridgeHttpResponse GatewayBridgeService::handlePost(
return handleGet("knx", gateway_id.value());
}
if (action == "knx_start") {
std::set<uint16_t> used_knx_ports;
ChannelRuntime* owner = selectKnxEndpointRuntime();
std::set<int> used_serial_uarts;
collectUsedRuntimeResources(gateway_id.value(), nullptr, &used_knx_ports,
&used_serial_uarts);
const esp_err_t err = runtime->startKnx(&used_knx_ports, &used_serial_uarts);
collectUsedRuntimeResources(owner == nullptr ? gateway_id.value() : owner->channel.gateway_id,
nullptr, nullptr, &used_serial_uarts);
const esp_err_t err = startKnxEndpoint(runtime, &used_serial_uarts);
if (err != ESP_OK) {
return ErrorResponse(err, runtime->knx_last_error.empty()
auto* owner = knx_endpoint_runtime_ != nullptr ? knx_endpoint_runtime_ : runtime;
return ErrorResponse(err, owner->knx_last_error.empty()
? "failed to start KNX/IP bridge"
: runtime->knx_last_error.c_str());
: owner->knx_last_error.c_str());
}
return handleGet("knx", gateway_id.value());
}
if (action == "knx_stop") {
const esp_err_t err = runtime->stopKnx();
const esp_err_t err = stopKnxEndpoint(runtime);
if (err != ESP_OK) {
return ErrorResponse(err, "failed to stop KNX/IP bridge");
}
+1 -1
View File
@@ -1,7 +1,7 @@
idf_component_register(
SRCS "src/gateway_knx.cpp"
INCLUDE_DIRS "include"
REQUIRES dali_cpp esp_driver_uart freertos log lwip openknx_idf
REQUIRES dali_cpp esp_driver_gpio esp_driver_uart esp_hw_support esp_netif freertos log lwip openknx_idf
)
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17)
+83 -7
View File
@@ -10,12 +10,14 @@
#include "lwip/sockets.h"
#include <atomic>
#include <array>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
@@ -62,6 +64,10 @@ struct GatewayKnxConfig {
uint16_t udp_port{kGatewayKnxDefaultUdpPort};
std::string multicast_address{kGatewayKnxDefaultMulticastAddress};
uint16_t individual_address{0x1101};
int programming_button_gpio{-1};
bool programming_button_active_low{true};
int programming_led_gpio{-1};
bool programming_led_active_high{true};
std::vector<GatewayKnxEtsAssociation> ets_associations;
GatewayKnxTpUartConfig tp_uart;
};
@@ -111,6 +117,7 @@ struct GatewayKnxCommissioningBallast {
std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value);
DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config);
bool GatewayKnxConfigUsesTpUart(const GatewayKnxConfig& config);
const char* GatewayKnxMappingModeToString(GatewayKnxMappingMode mode);
GatewayKnxMappingMode GatewayKnxMappingModeFromString(const std::string& value);
@@ -132,6 +139,8 @@ class GatewayKnxBridge {
size_t etsBindingCount() const;
std::vector<GatewayKnxDaliBinding> describeDaliBindings() const;
bool matchesCemiFrame(const uint8_t* data, size_t len) const;
bool matchesGroupAddress(uint16_t group_address) const;
DaliBridgeResult handleCemiFrame(const uint8_t* data, size_t len);
DaliBridgeResult handleGroupWrite(uint16_t group_address, const uint8_t* data,
size_t len);
@@ -186,13 +195,19 @@ class GatewayKnxBridge {
class GatewayKnxTpIpRouter {
public:
using CemiFrameHandler = std::function<DaliBridgeResult(const uint8_t* data, size_t len)>;
using GroupWriteHandler = std::function<DaliBridgeResult(uint16_t group_address,
const uint8_t* data,
size_t len)>;
GatewayKnxTpIpRouter(GatewayKnxBridge& bridge, CemiFrameHandler handler,
std::string openknx_namespace = "openknx");
~GatewayKnxTpIpRouter();
void setConfig(const GatewayKnxConfig& config);
void setCommissioningOnly(bool enabled);
void setGroupWriteHandler(GroupWriteHandler handler);
const GatewayKnxConfig& config() const;
bool tpUartOnline() const;
esp_err_t start(uint32_t task_stack_size, UBaseType_t task_priority);
esp_err_t stop();
@@ -201,17 +216,40 @@ class GatewayKnxTpIpRouter {
bool publishDaliStatus(const GatewayKnxDaliTarget& target, uint8_t actual_level);
private:
static constexpr size_t kMaxTunnelClients = 16;
struct TunnelClient {
bool connected{false};
uint8_t channel_id{0};
uint8_t connection_type{0};
uint8_t received_sequence{255};
uint8_t send_sequence{0};
uint16_t individual_address{0};
TickType_t last_activity_tick{0};
::sockaddr_in control_remote{};
::sockaddr_in data_remote{};
};
static void TaskEntry(void* arg);
esp_err_t initializeRuntime();
void taskLoop();
void finishTask();
void closeSockets();
bool configureSocket();
bool configureTpUart();
bool initializeTpUart();
bool configureProgrammingGpio();
void refreshNetworkInterfaces(bool force_log = false);
void handleUdpDatagram(const uint8_t* data, size_t len, const ::sockaddr_in& remote);
void handleSearchRequest(uint16_t service, const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void handleDescriptionRequest(const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void handleRoutingIndication(const uint8_t* body, size_t len);
void handleTunnellingRequest(const uint8_t* body, size_t len, const ::sockaddr_in& remote);
void handleDeviceConfigurationRequest(const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
void handleConnectRequest(const uint8_t* body, size_t len, const ::sockaddr_in& remote);
void handleConnectionStateRequest(const uint8_t* body, size_t len,
const ::sockaddr_in& remote);
@@ -220,47 +258,85 @@ class GatewayKnxTpIpRouter {
const ::sockaddr_in& remote);
void sendTunnellingAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
const ::sockaddr_in& remote);
void sendDeviceConfigurationAck(uint8_t channel_id, uint8_t sequence, uint8_t status,
const ::sockaddr_in& remote);
void sendConnectionHeaderAck(uint16_t service, uint8_t channel_id, uint8_t sequence,
uint8_t status, const ::sockaddr_in& remote);
void sendSecureSessionStatus(uint8_t status, const ::sockaddr_in& remote);
void sendTunnelIndication(const uint8_t* data, size_t len);
void sendTunnelIndicationToClient(TunnelClient& client, const uint8_t* data, size_t len);
void sendConnectionStateResponse(uint8_t channel_id, uint8_t status,
const ::sockaddr_in& remote);
void sendDisconnectResponse(uint8_t channel_id, uint8_t status,
const ::sockaddr_in& remote);
void sendConnectResponse(uint8_t channel_id, uint8_t status,
const ::sockaddr_in& remote);
const ::sockaddr_in& remote, uint8_t connection_type,
uint16_t tunnel_address);
void sendRoutingIndication(const uint8_t* data, size_t len);
bool handleOpenKnxTunnelFrame(const uint8_t* data, size_t len);
void sendSearchResponse(uint16_t service, const ::sockaddr_in& remote,
const std::set<uint8_t>& requested_dibs = {});
void sendDescriptionResponse(const ::sockaddr_in& remote);
std::array<uint8_t, 8> localHpaiForRemote(const ::sockaddr_in& remote) const;
std::vector<uint8_t> buildDeviceInfoDib(const ::sockaddr_in& remote) const;
std::vector<uint8_t> buildSupportedServiceDib() const;
std::vector<uint8_t> buildExtendedDeviceInfoDib() const;
std::vector<uint8_t> buildIpConfigDib(const ::sockaddr_in& remote, bool current) const;
std::vector<uint8_t> buildKnxAddressesDib() const;
std::vector<uint8_t> buildTunnelingInfoDib() const;
TunnelClient* findTunnelClient(uint8_t channel_id);
const TunnelClient* findTunnelClient(uint8_t channel_id) const;
TunnelClient* allocateTunnelClient(const ::sockaddr_in& control_remote,
const ::sockaddr_in& data_remote,
uint8_t connection_type);
void resetTunnelClient(TunnelClient& client);
uint8_t nextTunnelChannelId() const;
uint16_t effectiveTunnelAddressForSlot(size_t slot) const;
void pruneStaleTunnelClients();
bool handleOpenKnxTunnelFrame(const uint8_t* data, size_t len,
TunnelClient* response_client);
bool handleOpenKnxBusFrame(const uint8_t* data, size_t len);
void selectOpenKnxNetworkInterface(const ::sockaddr_in& remote);
bool emitOpenKnxGroupValue(uint16_t group_object_number, const uint8_t* data, size_t len);
bool shouldRouteDaliApplicationFrames() const;
void syncOpenKnxConfigFromDevice();
uint16_t effectiveIndividualAddress() const;
uint16_t effectiveTunnelAddress() const;
void pollTpUart();
void pollProgrammingButton();
void updateProgrammingLed();
void setProgrammingLed(bool on);
void handleTpUartControlByte(uint8_t byte);
void handleTpTelegram(const uint8_t* data, size_t len);
void forwardCemiToTp(const uint8_t* data, size_t len);
GatewayKnxBridge& bridge_;
CemiFrameHandler handler_;
GroupWriteHandler group_write_handler_;
std::string openknx_namespace_;
GatewayKnxConfig config_;
std::unique_ptr<openknx::EtsDeviceRuntime> ets_device_;
TaskHandle_t task_handle_{nullptr};
SemaphoreHandle_t openknx_lock_{nullptr};
SemaphoreHandle_t startup_semaphore_{nullptr};
esp_err_t startup_result_{ESP_OK};
std::atomic_bool stop_requested_{false};
std::atomic_bool started_{false};
int udp_sock_{-1};
int tp_uart_port_{-1};
uint8_t tunnel_channel_id_{1};
uint8_t expected_tunnel_sequence_{0};
uint8_t tunnel_send_sequence_{0};
bool tunnel_connected_{false};
::sockaddr_in tunnel_remote_{};
std::vector<uint32_t> multicast_joined_interfaces_;
TickType_t network_refresh_tick_{0};
std::array<TunnelClient, kMaxTunnelClients> tunnel_clients_{};
uint8_t last_tunnel_channel_id_{0};
std::vector<uint8_t> tp_rx_frame_;
std::vector<uint8_t> tp_last_sent_telegram_;
TickType_t tp_uart_last_byte_tick_{0};
bool tp_uart_extended_frame_{false};
bool tp_uart_online_{false};
bool commissioning_only_{false};
std::atomic_bool openknx_configured_{false};
bool programming_button_last_pressed_{false};
bool programming_led_state_{false};
TickType_t programming_button_last_toggle_tick_{0};
std::string last_error_;
};
File diff suppressed because it is too large Load Diff
@@ -560,7 +560,7 @@ std::optional<GatewayModbusConfig> GatewayModbusConfigFromValue(const DaliValue*
getObjectInt(json, "unitId").value_or(getObjectInt(json, "unit_id").value_or(1))));
if (const auto* serial_value = getObjectValue(json, "serial")) {
if (const auto* serial = serial_value->asObject()) {
config.serial.uart_port = clampedInt(*serial, "uartPort", config.serial.uart_port, 0, 2);
config.serial.uart_port = clampedInt(*serial, "uartPort", config.serial.uart_port, -1, 2);
config.serial.tx_pin = clampedInt(*serial, "txPin", config.serial.tx_pin, -1, 48);
config.serial.rx_pin = clampedInt(*serial, "rxPin", config.serial.rx_pin, -1, 48);
config.serial.baudrate = clampedU32(*serial, "baudrate", config.serial.baudrate,
@@ -56,8 +56,10 @@ struct GatewayNetworkServiceConfig {
bool status_led_active_high{true};
int boot_button_gpio{-1};
bool boot_button_active_low{true};
int setup_ap_button_gpio{-1};
bool setup_ap_button_active_low{true};
uint32_t boot_button_long_press_ms{3000};
uint32_t boot_button_task_stack_size{2048};
uint32_t boot_button_task_stack_size{8192};
UBaseType_t boot_button_task_priority{2};
uint32_t udp_task_stack_size{4096};
UBaseType_t udp_task_priority{4};
@@ -909,22 +909,37 @@ esp_err_t GatewayNetworkService::configureStatusLed() {
}
esp_err_t GatewayNetworkService::configureBootButton() {
if (config_.boot_button_gpio < 0) {
if (config_.boot_button_gpio < 0 && config_.setup_ap_button_gpio < 0) {
return ESP_OK;
}
gpio_config_t io_config = {};
io_config.pin_bit_mask = 1ULL << static_cast<uint32_t>(config_.boot_button_gpio);
io_config.mode = GPIO_MODE_INPUT;
io_config.pull_up_en = config_.boot_button_active_low ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
io_config.pull_down_en = config_.boot_button_active_low ? GPIO_PULLDOWN_DISABLE : GPIO_PULLDOWN_ENABLE;
io_config.intr_type = GPIO_INTR_DISABLE;
const esp_err_t err = gpio_config(&io_config);
const auto configure_input = [](int gpio, bool active_low, const char* name) -> esp_err_t {
if (gpio < 0) {
return ESP_OK;
}
gpio_config_t io_config = {};
io_config.pin_bit_mask = 1ULL << static_cast<uint32_t>(gpio);
io_config.mode = GPIO_MODE_INPUT;
io_config.pull_up_en = active_low ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
io_config.pull_down_en = active_low ? GPIO_PULLDOWN_DISABLE : GPIO_PULLDOWN_ENABLE;
io_config.intr_type = GPIO_INTR_DISABLE;
const esp_err_t err = gpio_config(&io_config);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to configure %s GPIO%d: %s", name, gpio, esp_err_to_name(err));
}
return err;
};
esp_err_t err = configure_input(config_.boot_button_gpio, config_.boot_button_active_low,
"Wi-Fi reset button");
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to configure boot button GPIO%d: %s", config_.boot_button_gpio,
esp_err_to_name(err));
return err;
}
return err;
if (config_.setup_ap_button_gpio == config_.boot_button_gpio) {
return ESP_OK;
}
return configure_input(config_.setup_ap_button_gpio, config_.setup_ap_button_active_low,
"setup AP button");
}
esp_err_t GatewayNetworkService::startHttpServer() {
@@ -985,7 +1000,8 @@ esp_err_t GatewayNetworkService::startUdpTask() {
}
esp_err_t GatewayNetworkService::startBootButtonTask() {
if (config_.boot_button_gpio < 0 || boot_button_task_handle_ != nullptr) {
if ((config_.boot_button_gpio < 0 && config_.setup_ap_button_gpio < 0) ||
boot_button_task_handle_ != nullptr) {
return ESP_OK;
}
@@ -1349,39 +1365,88 @@ void GatewayNetworkService::bootButtonTaskLoop() {
const TickType_t poll_ticks = pdMS_TO_TICKS(100);
const uint32_t long_press_ms = std::max<uint32_t>(config_.boot_button_long_press_ms, 100);
auto is_pressed = [this]() {
const int level = gpio_get_level(static_cast<gpio_num_t>(config_.boot_button_gpio));
return config_.boot_button_active_low ? level == 0 : level != 0;
auto is_pressed = [](int gpio, bool active_low) {
if (gpio < 0) {
return false;
}
const int level = gpio_get_level(static_cast<gpio_num_t>(gpio));
return active_low ? level == 0 : level != 0;
};
while (true) {
if (!is_pressed()) {
vTaskDelay(poll_ticks);
continue;
}
auto wait_release = [&](int gpio, bool active_low) {
uint32_t pressed_ms = 0;
while (is_pressed()) {
while (is_pressed(gpio, active_low)) {
vTaskDelay(poll_ticks);
pressed_ms += 100;
}
return pressed_ms;
};
if (pressed_ms >= long_press_ms) {
ESP_LOGW(kTag, "BOOT long press clears Wi-Fi credentials and restarts");
runtime_.clearWirelessInfo();
stopEspNow();
if (wifi_started_) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
auto enter_setup_ap = [this]() {
ESP_LOGI(kTag, "setup AP button enters setup AP mode");
const uint32_t stack_size = std::max<uint32_t>(config_.boot_button_task_stack_size, 8192);
const BaseType_t created = xTaskCreate(
[](void* arg) {
auto* service = static_cast<GatewayNetworkService*>(arg);
service->handleWifiControl(101);
vTaskDelete(nullptr);
},
"gateway_setup_ap", stack_size, this, config_.boot_button_task_priority, nullptr);
if (created != pdPASS) {
ESP_LOGE(kTag, "failed to create setup AP task");
}
};
auto clear_wifi_and_restart = [this]() {
ESP_LOGW(kTag, "Wi-Fi reset button clears Wi-Fi credentials and restarts");
runtime_.clearWirelessInfo();
stopEspNow();
if (wifi_started_) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
}
vTaskDelay(pdMS_TO_TICKS(300));
esp_restart();
};
while (true) {
const bool same_button = config_.boot_button_gpio >= 0 &&
config_.boot_button_gpio == config_.setup_ap_button_gpio;
if (same_button && is_pressed(config_.boot_button_gpio, config_.boot_button_active_low)) {
const uint32_t pressed_ms = wait_release(config_.boot_button_gpio,
config_.boot_button_active_low);
if (pressed_ms >= long_press_ms) {
clear_wifi_and_restart();
} else {
enter_setup_ap();
}
vTaskDelay(pdMS_TO_TICKS(300));
esp_restart();
} else {
ESP_LOGI(kTag, "BOOT short press enters setup AP mode");
handleWifiControl(101);
continue;
}
vTaskDelay(pdMS_TO_TICKS(300));
if (is_pressed(config_.setup_ap_button_gpio, config_.setup_ap_button_active_low)) {
wait_release(config_.setup_ap_button_gpio, config_.setup_ap_button_active_low);
enter_setup_ap();
vTaskDelay(pdMS_TO_TICKS(300));
continue;
}
if (is_pressed(config_.boot_button_gpio, config_.boot_button_active_low)) {
const uint32_t pressed_ms = wait_release(config_.boot_button_gpio,
config_.boot_button_active_low);
if (pressed_ms >= long_press_ms) {
clear_wifi_and_restart();
}
vTaskDelay(pdMS_TO_TICKS(300));
continue;
}
if (config_.setup_ap_button_gpio < 0 && config_.boot_button_gpio < 0) {
vTaskDelete(nullptr);
return;
}
vTaskDelay(poll_ticks);
}
}
@@ -29,11 +29,15 @@ class EtsDeviceRuntime {
uint16_t individualAddress() const;
uint16_t tunnelClientAddress() const;
bool configured() const;
bool programmingMode() const;
void setProgrammingMode(bool enabled);
void toggleProgrammingMode();
EtsMemorySnapshot snapshot() const;
void setFunctionPropertyHandlers(FunctionPropertyHandler command_handler,
FunctionPropertyHandler state_handler);
void setGroupWriteHandler(GroupWriteHandler handler);
void setNetworkInterface(esp_netif_t* netif);
bool handleTunnelFrame(const uint8_t* data, size_t len, CemiFrameSender sender);
bool handleBusFrame(const uint8_t* data, size_t len);
@@ -20,12 +20,32 @@ namespace {
constexpr const char* kTag = "openknx_idf";
constexpr const char* kEepromKey = "eeprom";
esp_netif_t* findDefaultNetif() {
if (auto* sta = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF")) {
return sta;
bool readBaseMac(uint8_t* data) {
if (data == nullptr) {
return false;
}
if (auto* eth = esp_netif_get_handle_from_ifkey("ETH_DEF")) {
return eth;
if (esp_efuse_mac_get_default(data) == ESP_OK) {
return true;
}
return esp_read_mac(data, ESP_MAC_WIFI_STA) == ESP_OK;
}
esp_netif_t* findDefaultNetif() {
constexpr const char* kPreferredIfKeys[] = {"ETH_DEF", "WIFI_STA_DEF", "WIFI_AP_DEF"};
for (const char* key : kPreferredIfKeys) {
auto* netif = esp_netif_get_handle_from_ifkey(key);
if (netif == nullptr || !esp_netif_is_netif_up(netif)) {
continue;
}
esp_netif_ip_info_t ip_info{};
if (esp_netif_get_ip_info(netif, &ip_info) == ESP_OK && ip_info.ip.addr != 0) {
return netif;
}
}
for (const char* key : kPreferredIfKeys) {
if (auto* netif = esp_netif_get_handle_from_ifkey(key)) {
return netif;
}
}
return nullptr;
}
@@ -103,7 +123,7 @@ void EspIdfPlatform::macAddress(uint8_t* data) {
if (data == nullptr) {
return;
}
if (esp_read_mac(data, ESP_MAC_WIFI_STA) != ESP_OK) {
if (!readBaseMac(data)) {
std::memset(data, 0, 6);
}
}
@@ -111,7 +131,7 @@ void EspIdfPlatform::macAddress(uint8_t* data) {
uint32_t EspIdfPlatform::uniqueSerialNumber() {
uint8_t mac[6]{};
macAddress(mac);
return (static_cast<uint32_t>(mac[0]) << 24) | (static_cast<uint32_t>(mac[1]) << 16) |
return (static_cast<uint32_t>(mac[2]) << 24) | (static_cast<uint32_t>(mac[3]) << 16) |
(static_cast<uint32_t>(mac[4]) << 8) | mac[5];
}
@@ -37,6 +37,13 @@ bool IsUsableIndividualAddress(uint16_t address) {
return address != 0 && address != kInvalidIndividualAddress;
}
bool IsErasedMemory(const uint8_t* data, size_t size) {
if (data == nullptr || size == 0) {
return true;
}
return std::all_of(data, data + size, [](uint8_t value) { return value == 0xff; });
}
void ApplyReg1DaliIdentity(Bau07B0& device, EspIdfPlatform& platform) {
device.deviceObject().manufacturerId(kReg1DaliManufacturerId);
device.deviceObject().bauNumber(platform.uniqueSerialNumber());
@@ -58,7 +65,11 @@ EtsDeviceRuntime::EtsDeviceRuntime(std::string nvs_namespace,
if (IsUsableIndividualAddress(fallback_individual_address)) {
device_.deviceObject().individualAddress(fallback_individual_address);
}
device_.readMemory();
const uint8_t* memory = platform_.getNonVolatileMemoryStart();
const size_t memory_size = platform_.getNonVolatileMemorySize();
if (!IsErasedMemory(memory, memory_size)) {
device_.readMemory();
}
if (!IsUsableIndividualAddress(device_.deviceObject().individualAddress()) &&
IsUsableIndividualAddress(fallback_individual_address)) {
device_.deviceObject().individualAddress(fallback_individual_address);
@@ -99,6 +110,16 @@ uint16_t EtsDeviceRuntime::tunnelClientAddress() const {
bool EtsDeviceRuntime::configured() const { return const_cast<Bau07B0&>(device_).configured(); }
bool EtsDeviceRuntime::programmingMode() const {
return const_cast<Bau07B0&>(device_).deviceObject().progMode();
}
void EtsDeviceRuntime::setProgrammingMode(bool enabled) {
device_.deviceObject().progMode(enabled);
}
void EtsDeviceRuntime::toggleProgrammingMode() { setProgrammingMode(!programmingMode()); }
EtsMemorySnapshot EtsDeviceRuntime::snapshot() const {
EtsMemorySnapshot out;
auto& device = const_cast<Bau07B0&>(device_);
@@ -139,6 +160,10 @@ void EtsDeviceRuntime::setGroupWriteHandler(GroupWriteHandler handler) {
group_write_handler_ = std::move(handler);
}
void EtsDeviceRuntime::setNetworkInterface(esp_netif_t* netif) {
platform_.networkInterface(netif);
}
bool EtsDeviceRuntime::handleTunnelFrame(const uint8_t* data, size_t len,
CemiFrameSender sender) {
auto* server = device_.getCemiServer();
@@ -289,6 +314,9 @@ bool EtsDeviceRuntime::shouldConsumeTunnelFrame(CemiFrame& frame) const {
case M_FuncPropStateRead_req:
return true;
case L_data_req:
if (!const_cast<Bau07B0&>(device_).configured() || programmingMode()) {
return true;
}
if (frame.addressType() == IndividualAddress &&
frame.destinationAddress() == individualAddress()) {
return true;
+79 -41
View File
@@ -2,8 +2,8 @@
#include "esp_log.h"
#include "esp_mac.h"
#include "esp_random.h"
#include "esp_timer.h"
#include "mbedtls/sha256.h"
#include "nvs.h"
#include "nvs_flash.h"
@@ -22,11 +22,13 @@ constexpr const char* kFactoryFdskKey = "factory_fdsk";
constexpr size_t kFdskSize = 16;
constexpr size_t kSerialSize = 6;
constexpr size_t kFdskQrSize = 36;
constexpr uint16_t kKnxManufacturerId = 0x00A4;
constexpr const char* kProductIdentity = "REG1-Dali";
constexpr const char* kManufacturerId = "00A4";
constexpr const char* kApplicationNumber = "01";
constexpr const char* kApplicationVersion = "05";
constexpr const char* kDevelopmentStorage = "plain_nvs_development";
constexpr const char* kDevelopmentStorage = "base_mac_derived_plain_nvs_development";
constexpr char kFdskDerivationLabel[] = "DaliMaster REG1-Dali deterministic FDSK v1";
constexpr uint8_t kCrc4Tab[16] = {
0x0, 0x3, 0x6, 0x5, 0xc, 0xf, 0xa, 0x9,
0xb, 0x8, 0xd, 0xe, 0x7, 0x4, 0x1, 0x2,
@@ -57,10 +59,14 @@ bool plausibleKey(const uint8_t* data) {
return !all_zero && !all_ff;
}
void generateKey(uint8_t* data) {
do {
esp_fill_random(data, kFdskSize);
} while (!plausibleKey(data));
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() {
@@ -108,16 +114,60 @@ bool parseHexKey(const std::string& value, uint8_t* out) {
return plausibleKey(out);
}
bool storeFactoryFdsk(const uint8_t* data) {
if (data == nullptr || !plausibleKey(data) || !ensureNvsReady()) {
bool loadKnxSerialNumber(uint8_t* serial) {
if (serial == nullptr) {
return false;
}
std::array<uint8_t, kSerialSize> mac{};
if (!readBaseMac(mac.data())) {
return false;
}
serial[0] = static_cast<uint8_t>((kKnxManufacturerId >> 8) & 0xff);
serial[1] = static_cast<uint8_t>(kKnxManufacturerId & 0xff);
std::copy(mac.begin() + 2, mac.end(), serial + 2);
return true;
}
bool deriveFactoryFdskFromSerial(const uint8_t* serial, uint8_t* key) {
if (serial == nullptr || key == nullptr) {
return false;
}
std::array<uint8_t, sizeof(kFdskDerivationLabel) - 1 + kSerialSize> material{};
std::copy(kFdskDerivationLabel, kFdskDerivationLabel + sizeof(kFdskDerivationLabel) - 1,
material.begin());
std::copy(serial, serial + kSerialSize, material.begin() + sizeof(kFdskDerivationLabel) - 1);
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 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(kNamespace, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGW(kTag, "failed to open KNX security NVS namespace: %s", esp_err_to_name(err));
return false;
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) {
@@ -125,11 +175,10 @@ bool storeFactoryFdsk(const uint8_t* data) {
}
nvs_close(handle);
if (err != ESP_OK) {
ESP_LOGW(kTag, "failed to store KNX factory FDSK: %s", esp_err_to_name(err));
return false;
ESP_LOGW(kTag, "failed to mirror deterministic KNX factory FDSK: %s", esp_err_to_name(err));
return;
}
clearOpenKnxFdskCache();
return true;
}
uint8_t crc4Array(const uint8_t* data, size_t len) {
@@ -219,35 +268,18 @@ std::string fnv1aHex(const std::string& value) {
namespace gateway::openknx {
bool LoadFactoryFdsk(uint8_t* data, size_t len) {
if (data == nullptr || len < kFdskSize || !ensureNvsReady()) {
if (data == nullptr || len < kFdskSize) {
return false;
}
nvs_handle_t handle = 0;
esp_err_t err = nvs_open(kNamespace, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGW(kTag, "failed to open KNX security NVS namespace: %s", esp_err_to_name(err));
return false;
}
size_t stored_size = kFdskSize;
err = nvs_get_blob(handle, kFactoryFdskKey, data, &stored_size);
if (err == ESP_OK && stored_size == kFdskSize && plausibleKey(data)) {
nvs_close(handle);
return true;
}
generateKey(data);
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 store generated KNX factory FDSK: %s", esp_err_to_name(err));
std::array<uint8_t, kSerialSize> serial{};
std::array<uint8_t, kFdskSize> key{};
if (!loadKnxSerialNumber(serial.data()) ||
!deriveFactoryFdskFromSerial(serial.data(), key.data())) {
return false;
}
std::memcpy(data, key.data(), kFdskSize);
syncFactoryFdskToNvs(key.data());
return true;
}
@@ -255,8 +287,7 @@ FactoryFdskInfo LoadFactoryFdskInfo() {
FactoryFdskInfo info;
std::array<uint8_t, kFdskSize> key{};
std::array<uint8_t, kSerialSize> serial{};
if (!LoadFactoryFdsk(key.data(), key.size()) ||
esp_read_mac(serial.data(), ESP_MAC_WIFI_STA) != ESP_OK) {
if (!loadKnxSerialNumber(serial.data()) || !LoadFactoryFdsk(key.data(), key.size())) {
return info;
}
@@ -269,8 +300,7 @@ FactoryFdskInfo LoadFactoryFdskInfo() {
bool GenerateFactoryFdsk(FactoryFdskInfo* info) {
std::array<uint8_t, kFdskSize> key{};
generateKey(key.data());
const bool stored = storeFactoryFdsk(key.data());
const bool stored = LoadFactoryFdsk(key.data(), key.size());
std::fill(key.begin(), key.end(), 0);
if (!stored) {
return false;
@@ -286,8 +316,16 @@ bool WriteFactoryFdskHex(const std::string& hex_key, FactoryFdskInfo* info) {
if (!parseHexKey(hex_key, key.data())) {
return false;
}
const bool stored = storeFactoryFdsk(key.data());
std::array<uint8_t, kSerialSize> serial{};
std::array<uint8_t, kFdskSize> derived{};
const bool stored = loadKnxSerialNumber(serial.data()) &&
deriveFactoryFdskFromSerial(serial.data(), derived.data()) &&
std::equal(key.begin(), key.end(), derived.begin());
if (stored) {
syncFactoryFdskToNvs(derived.data());
}
std::fill(key.begin(), key.end(), 0);
std::fill(derived.begin(), derived.end(), 0);
if (!stored) {
return false;
}