- 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>
Gateway Rewrite
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.gateway_core/: boot profile and top-level role bootstrap.dali/: vendored ESP-IDF DALI HAL/backend reused from LuatOS, including native raw receive fan-out.dali_domain/: native DALI domain facade overdali_cppand raw frame sinks.gateway_cache/: DALI scene/group/settings/runtime cache used by controller reconciliation and protocol bridges.gateway_bridge/: per-channel bridge provisioning, command execution, protocol startup, and HTTP bridge actions.openknx_idf/: ESP-IDF port layer for the OpenKNXgateway/knxandgateway/tpuartsubmodules, including NVS-backed OpenKNX memory, development KNX security storage, ETS cEMI programming support, UDP multicast/unicast plumbing, and a native TP-UART interface without the Arduino framework.gateway_modbus/: gateway-owned Modbus TCP/RTU/ASCII config, generated DALI point tables, and provisioned Modbus model override dispatch.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 onFFF1/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 port2020command/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 framed0x28 0x01commands and0x22 ... checksumnotifications at9600 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, setup AP GPIO entry, and optional Wi-Fi credential reset GPIO handling, and an optional gateway_485_control bridge that claims UART0 for Lua-compatible framed command ingress plus 0x22 notification egress when the console is moved off UART0. Startup behavior is configured in main/Kconfig.projbuild: BLE and wired Ethernet are enabled by default, W5500 initialization and startup probe failures are ignored by default for boards without populated Ethernet hardware by fully disabling Ethernet for that boot, Wi-Fi STA, smartconfig, and ESP-NOW setup mode are disabled by default, the built-in USB Serial/JTAG interface stays in debug mode unless the optional USB setup bridge mode is selected, and the UART0 control bridge stays disabled unless the deployment explicitly repurposes UART0 away from the ESP-IDF console. Runtime settings and internal scene/group data are cached in RAM after load, skip unchanged flash writes, and batch Wi-Fi credential commits to reduce flash stalls on ESP32-S3 boards where flash and PSRAM share the SPI bus. The gateway app exposes per-channel PHY selection through main/Kconfig.projbuild; each channel can be disabled, bound to the native DALI GPIO HAL, or bound to a UART1/UART2 serial PHY. The checked-in sdkconfig is aligned with the app's custom 16 MB partition table so the Wi-Fi/BLE/network-enabled image fits the OTA app slots.
KNX Security
KNX Data Secure and KNXnet/IP Secure support are controlled by GATEWAY_KNX_DATA_SECURE_SUPPORTED and GATEWAY_KNX_IP_SECURE_SUPPORTED. The current KNXnet/IP Secure flag reserves and reports secure service capability, while runtime secure-session transport is still reported as not implemented until that path is wired. The gateway derives its KNX serial identity from the ESP base MAC, and the development factory setup key is deterministically derived from that KNX serial so the same board keeps the same FDSK across NVS erases.
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.
The normal bridge status response includes a knx.security object with compile-time capability flags, storage mode, factory setup key metadata, factory certificate metadata, and security failure counters/log entries. Secret FDSK strings are returned only by the explicit development actions, not by passive status polling.
Modbus
Modbus TCP, RTU, and ASCII are owned by gateway/components/gateway_modbus and started through the per-channel bridge service. The gateway keeps the existing bridge config JSON shape with a top-level modbus object containing transport, host, port, and unitID, and now adds nested serial UART settings for RTU/ASCII. Parsing and runtime behavior live in the gateway project rather than in dali_cpp.
See MODBUS.md for transport setup, UART0 policy, RS485 wiring, runtime @DALIGW management commands, supported function codes, and the full generated address map with address formulas.
The first generated map slice creates stable points for every DALI short address 0-63 whether the device is online, offline, or never seen. Per short address, the generated map reserves a 32-point stride in each Modbus space:
- Coils: command triggers such as on, off, recall max, and recall min.
- Discrete inputs: inventory, online, supported device-type, cache-known, and base status bit positions.
- Holding registers: writable brightness, color temperature, group mask, power-on level, system-failure level, min/max level, and fade time.
- Input registers: read-only inventory state, primary type, type mask, cached actual level, scene id, raw status placeholder, group mask, and cached settings.
Unknown numeric values read as 0xFFFF; booleans read as false unless inventory or cache state proves otherwise. Provisioned Modbus models still work as overrides at their configured Modbus point, and normal generated reads prefer gateway cache state to avoid DALI bus polling.
An extension discrete-input range starts immediately after the legacy 0-63 short-address block. It publishes decoded DALI status, failure, and feature bits as individual booleans for base status, DT1 emergency, DT4/5/6 control-gear feature/failure status, and DT8 color status/features. This keeps existing Modbus addresses stable while making bit-level diagnostics readable without consumers masking packed status registers.
BACnet/IP
BACnet/IP is owned by gateway/components/gateway_bacnet and is started through the per-channel bridge service. Runtime BACnet server settings live under top-level bacnetServer in bridge config:
{
"bacnetServer": {
"deviceInstance": 4194303,
"localAddress": "",
"udpPort": 47808
}
}
Provisioned BACnet models still use generic BridgeModel fields such as object type, object instance, property, and optional bitIndex. Query-style models refresh BACnet Present_Value from live DALI reads, and binary models with bitIndex expose a single packed status bit.
For discovered DALI short addresses, the gateway also mirrors the generated Modbus discrete diagnostics as BACnet binary-input objects. Object instances are allocated in a gateway-owned generated range using the channel index plus the generated Modbus discrete-input offset, so generated objects stay deterministic while avoiding the provisioned-object address space in normal deployments.