Chapter 208: KNX/EIB Protocol Integration

Chapter Objectives

Upon completing this chapter, you will be able to:

  • Understand the fundamental principles and architecture of the KNX protocol.
  • Identify different KNX physical layers and communication media (TP, PL, RF, IP).
  • Describe the structure of a KNX telegram and the significance of addressing (Physical and Group Addresses).
  • Recognize the role of Data Point Types (DPTs) in ensuring interoperability.
  • Understand methods for interfacing an ESP32 microcontroller with a KNX bus, primarily focusing on KNX TP (via TPUART) and KNX IP.
  • Develop basic C code for ESP32 to send and receive KNX messages.
  • Appreciate the differences and considerations when using various ESP32 variants for KNX integration.
  • Troubleshoot common issues encountered during KNX integration projects.

Introduction

KNX (Konnex) is a globally recognized, open standard (ISO/IEC 14543-3) for home and building control. It evolved from the convergence of earlier standards like EIB (European Installation Bus), EHS (European Home Systems Protocol), and BatiBUS. KNX offers a robust, decentralized, and interoperable solution for automating various functions in buildings, such as lighting, HVAC (Heating, Ventilation, and Air Conditioning), shutter/blind control, security systems, energy management, and more.

The ESP32, with its powerful processing capabilities, integrated Wi-Fi and Bluetooth, and flexible peripheral set, is an excellent candidate for creating custom KNX devices, gateways, or visualization tools. Integrating ESP32 with KNX allows developers to bridge the world of IoT with established building automation systems, enabling innovative applications like voice control of KNX devices, cloud connectivity for remote management, or sophisticated data logging and analytics. This chapter will guide you through the essentials of the KNX protocol and demonstrate how to integrate your ESP32 projects into KNX networks.

Theory

What is KNX?

KNX is a communication protocol designed specifically for building automation. Its key characteristics include:

  • Standardization: It’s an international standard, ensuring compatibility between products from different manufacturers. Over 500 manufacturers produce KNX-certified devices.
  • Decentralization: In a KNX system, intelligence is distributed among the devices. Each device has its own microcontroller and can make decisions or act based on received messages. There’s no single central controller whose failure would bring down the entire system (though central units can be used for advanced functions).
  • Interoperability: Devices from different manufacturers can seamlessly work together in the same installation, provided they are KNX certified and configured correctly.
  • Flexibility and Scalability: KNX systems can range from small home installations to large commercial building complexes. The topology allows for easy expansion.
  • ETS (Engineering Tool Software): A manufacturer-independent software tool used for designing, commissioning, and diagnosing KNX installations.

KNX System Architecture

A KNX system is structured hierarchically to manage communication across potentially large installations.

  • Devices:
    • Sensors: Collect information (e.g., temperature sensors, motion detectors, push buttons, weather stations).
    • Actuators: Perform actions (e.g., switching relays for lights, controlling blind motors, adjusting valve positions for heating).
    • System Devices: Manage the bus itself or provide infrastructure. Examples include:
      • Power Supplies: Provide the necessary SELV (Safety Extra Low Voltage, typically 24-30V DC) power to the KNX bus line and devices connected to it.
      • Line Couplers (LC) / Backbone Couplers (BC): Connect different parts of the KNX system, filtering telegrams to reduce bus load. An LC connects a line to a main line, and a BC connects a main line to the backbone line.
      • Interfaces: USB interfaces for programming via ETS, or IP interfaces/routers for connecting to IP networks.
  • Topology:
    • Line: The smallest segment, typically comprising up to 64 bus devices powered by one KNX power supply. Each line can be extended with up to 3 line repeaters, allowing up to 256 devices on a logical line.
    • Area: Consists of up to 15 lines connected via Line Couplers to a main line.
    • Backbone: Connects up to 15 areas via Backbone Couplers.This hierarchical structure (Area -> Main Line -> Line -> Device) allows for extensive installations.
graph TD
    subgraph Backbone Area
        BB[Backbone Line]
    end

    subgraph Area 1
        direction TB
        M1[Main Line 1]
        L1_1[Line 1.1]
        L1_2[Line 1.2]
    end
    
    subgraph Area 2
        direction TB
        M2[Main Line 2]
        L2_1[Line 2.1]
    end

    subgraph "Line 1.1 Devices"
        D1_1_1(Sensor 1.1.1)
        D1_1_2(Actuator 1.1.2)
        D1_1_3(Push Button 1.1.3)
    end
    
    BB -- "Backbone Coupler" --> M1
    BB -- "Backbone Coupler" --> M2

    M1 -- "Line Coupler" --> L1_1
    M1 -- "Line Coupler" --> L1_2
    M2 -- "Line Coupler" --> L2_1
    
    L1_1 --> D1_1_1
    L1_1 --> D1_1_2
    L1_1 --> D1_1_3

    classDef default fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF
    classDef area fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6
    classDef device fill:#D1FAE5,stroke:#059669,stroke-width:1px,color:#065F46

    class BB,M1,M2 area
    class D1_1_1,D1_1_2,D1_1_3 device

KNX Communication Media

KNX supports several communication media:

Media Physical Layer Data Rate Primary Use Case
KNX TP (Twisted Pair) Dedicated 2-wire bus cable (typically green) 9600 bit/s New installations; robust and reliable communication where data and power are on the same cable. The most common medium.
KNX PL (Power Line) Existing mains wiring (110/230V AC) 1200 bit/s Retrofitting older buildings where running new cables is impractical or impossible.
KNX RF (Radio Frequency) Wireless radio signals (e.g., 868 MHz in Europe) Varies (typically >16 kbit/s) Extending installations to locations hard to wire; integrating battery-powered or mobile devices.
KNX IP (Ethernet/IP) Standard IP networks (Ethernet, Wi-Fi) High (10/100+ Mbit/s) Backbone for connecting KNX TP lines/areas, integration with IT systems, gateways, and visualization tools like an ESP32.
  1. Twisted Pair (KNX TP):
    • The most common medium. Uses a dedicated two-wire bus cable (typically a green KNX-certified cable).
    • Data and power (SELV 29V DC) are transmitted over the same pair of wires.
    • Data rate is 9600 bit/s.
    • This is the primary focus for many ESP32 integrations using dedicated transceiver ICs.
  2. Power Line (KNX PL):
    • Transmits data over existing 110/230V AC mains wiring.
    • Useful for retrofitting buildings where new cabling is not feasible.
    • Data rate is 1200 bit/s.
  3. Radio Frequency (KNX RF):
    • Wireless communication, typically in the 868 MHz band (in Europe).
    • Suitable for areas where wiring is difficult or for battery-operated devices.
    • KNX RF Ready and KNX RF Multi standards exist, offering different capabilities.
  4. Ethernet (KNX IP):
    • Uses standard IP networks (Ethernet or Wi-Fi) for communication.
    • Often used for connecting different KNX TP installations (e.g., in different buildings) via an IP backbone, or for interfacing with visualization systems, gateways, or software like ETS.
    • Two main modes:
      • KNX IP Routing: Uses IP multicast (UDP port 3671) to transmit KNX telegrams directly over IP. Devices act as peers.
      • KNX IP Tunneling: Uses a point-to-point UDP connection (port 3671) to a KNX IP Router or Interface. The ESP32 would act as a client, and the KNX IP device “tunnels” KNX TP telegrams to/from the IP network. This is often simpler for client applications.

KNX Telegram Structure (Focus on TP)

A KNX telegram is the data packet exchanged between KNX devices. For KNX TP, the structure is well-defined:

Field Length Description
Control Field 8 bits Defines the frame type, repetition status, and message priority.
Source Address 16 bits The unique Physical Address of the sending device (e.g., 1.1.10).
Destination Address 16 bits Can be a Physical Address for direct communication or a Group Address for functional communication.
Routing Counter 3 bits Starts at a value (e.g., 6) and is decremented by each line/backbone coupler it passes through. A telegram with a counter of 0 is discarded.
Length 5 bits Specifies the length of the following payload in bytes (a value of 0 means 1 byte, up to 15 for 16 bytes).
Payload 1 to 16 bytes Contains the core application data:
  • APCI: (min 10 bits) Specifies the command (e.g., Read, Write, Response).
  • Data: The actual value, formatted according to a specific Data Point Type (DPT).
Checksum 8 bits An LRC (Longitudinal Redundancy Check) used to detect errors in the telegram.

Addressing:

Address Type Purpose Format Example
Physical Address (Individual Address) Uniquely identifies a single device on the bus for programming, diagnostics, and management. It’s the device’s “house number”. Area.Line.Device
(e.g., 4.4.8 bits)
1.1.10 identifies device #10 on line #1 of area #1.
Group Address (GA) Links devices functionally. Represents a command or piece of information, not a device. It’s the “function call”. Main/Middle/Sub or Main/Sub 1/0/1 could represent “Switch Living Room Ceiling Light”. Multiple devices can listen and react to this address.
  • Physical Address (Individual Address): Uniquely identifies each device on the bus. Format: Area.Line.Device (e.g., 1.1.10).
    • Area: 4 bits (0-15)
    • Line: 4 bits (0-15)
    • Device: 8 bits (0-255, though typically 1-64 per line segment before repeaters)
    • Physical addresses are primarily used during commissioning (ETS programming) and for some direct device management.
  • Group Address (GA): Represents a function or a group of devices that should react to a command (e.g., “Living Room Lights,” “All Blinds Down”). Devices are configured (via ETS) to listen to specific group addresses. This is the primary method for operational communication.
    • Format can be 2-level (Main/Sub) or 3-level (Main/Middle/Sub), e.g., 1/2/3.
    • Example: A push button sends a telegram to Group Address 1/0/1. All light actuators programmed to listen to 1/0/1 will react.

Data Point Types (DPTs)

To ensure devices from different manufacturers can understand each other, KNX defines standardized Data Point Types (DPTs). A DPT specifies the data format and interpretation for a particular type of information.

DPT Number Function Data Format Example Value
DPT 1.001 Switching (On/Off) 1 bit 0 = Off, 1 = On
DPT 3.007 Dimming (Up/Down) 4 bits (1 bit control, 3 bits step) 0b1101 = Increase brightness by 1/8
DPT 5.001 Scaling / Percentage 8 bits (1 byte) 0xFF (255) = 100%, 0x80 (128) = 50%
DPT 9.001 Temperature (°C) 16 bits (2 byte signed float) 0x099A represents 24.58°C
DPT 20.102 HVAC Mode 8 bits (1 byte enum) 0=Auto, 1=Comfort, 3=Night

When a device sends data (e.g., temperature value), it uses the DPT defined for that function’s group address. Receiving devices configured for that DPT can correctly interpret the data. ETS is used to link group addresses with appropriate DPTs.

KNX Commissioning

Setting up a KNX installation is done using the ETS (Engineering Tool Software). This software allows an installer or system integrator to:

  • Design the project topology.
  • Import product databases from manufacturers.
  • Assign physical addresses to devices.
  • Create group addresses and link them to device communication objects (representing functions like switching, dimming, sending sensor values).
  • Configure device parameters.
  • Download the configuration to the devices.
  • Diagnose and troubleshoot the installation.

An ESP32 acting as a KNX device would also need its communication objects and group address associations configured conceptually, even if not directly programmed by ETS in a standard way (unless it’s a certified KNX device or uses a KNX stack that supports this).

Interfacing ESP32 with KNX

1. KNX TP (Twisted Pair) via TPUART

The most direct way to connect an ESP32 to the KNX TP bus is by using a KNX Bus Coupler Unit (BCU) or a TPUART (Twisted Pair Universal Asynchronous Receiver/Transmitter) IC.

  • Examples of TPUART ICs: Siemens TPUART, NCN5120/NCN5121/NCN5130 (onsemi), FZE1066.
  • These ICs handle the low-level physical layer requirements of the KNX TP bus (signal levels, collision detection, telegram framing/deframing, power extraction if a BCU).
  • The ESP32 communicates with the TPUART IC via a standard UART interface (RX, TX lines).
  • The TPUART module typically converts the KNX telegrams into a serial protocol that the ESP32 can easily send and receive. Some TPUARTs offer different modes (e.g., bit-oriented, character-oriented, or a more abstracted frame mode).
sequenceDiagram
    participant ESP32
    participant TPUART Module
    participant KNX Bus

    ESP32->>TPUART Module: 1. Send command via UART (e.g., "Write GA 1/1/1 with value ON")
    activate TPUART Module
    TPUART Module->>KNX Bus: 2. Formats & transmits KNX Telegram
    deactivate TPUART Module
    
    activate KNX Bus
    KNX Bus-->>TPUART Module: 3. Receives Telegram (e.g., from another device)
    deactivate KNX Bus
    
    activate TPUART Module
    TPUART Module->>ESP32: 4. Decodes Telegram & sends info via UART
    deactivate TPUART Module


Communication Flow (ESP32 to TPUART):

  1. Sending: The ESP32 constructs a KNX message payload (destination GA, DPT, data) according to the TPUART’s specific serial protocol. It then sends this formatted data packet over UART to the TPUART. The TPUART handles the transmission onto the KNX bus, including checksum calculation, preambles, and collision detection.
  2. Receiving: The TPUART listens on the KNX bus. When it receives a valid telegram addressed to it (or a group address it’s listening to, depending on its configuration mode), it decodes it and sends the relevant parts (source PA, destination GA, DPT, data) to the ESP32 via UART.

Tip: When using a TPUART module, carefully study its datasheet. The exact serial protocol, baud rate (often 19200 for TPUART2, but can vary), and control commands differ between modules.

2. KNX IP

For KNX IP, the ESP32 uses its Wi-Fi or Ethernet capabilities.

  • KNX IP Tunneling:
    • The ESP32 acts as a client connecting to a KNX IP Router or KNX IP Interface on the network.
    • Communication typically uses UDP on port 3671.
    • The ESP32 needs to implement a subset of the KNXnet/IP protocol to:
      1. Discover KNX IP interfaces (optional, via Search Request/Response).
      2. Establish a control connection (Connect Request/Response).
      3. Request a tunneling channel (Tunneling Connect Request/Response).
      4. Send KNX telegrams encapsulated in Tunneling Requests.
      5. Receive KNX telegrams encapsulated in Tunneling Requests from the KNX IP device.
      6. Send acknowledgements (Tunneling Ack).
      7. Maintain the connection (Heartbeat/State Request/Response).
    • This is generally simpler than full IP Routing for a single device.
sequenceDiagram
    participant ESP32
    participant "KNX IP Router"

    ESP32->>"KNX IP Router": 1. CONNECT_REQUEST
    activate "KNX IP Router"
    "KNX IP Router"-->>ESP32: 2. CONNECT_RESPONSE (Status: OK, ChannelID: 1)
    deactivate "KNX IP Router"
    
    Note over ESP32, "KNX IP Router": Connection Established!

    ESP32->>"KNX IP Router": 3. TUNNELLING_REQUEST (ChannelID: 1, Seq: 0, cEMI: Write "ON" to GA 1/1/1)
    activate "KNX IP Router"
    "KNX IP Router"-->>ESP32: 4. TUNNELLING_ACK (ChannelID: 1, Seq: 0, Status: OK)
    deactivate "KNX IP Router"

    loop Heartbeat
        ESP32->>"KNX IP Router": 5. CONNECTIONSTATE_REQUEST (ChannelID: 1)
        activate "KNX IP Router"
        "KNX IP Router"-->>ESP32: 6. CONNECTIONSTATE_RESPONSE (Status: OK)
        deactivate "KNX IP Router"
    end
    

  • KNX IP Routing:
    • The ESP32 would join a specific multicast group (default: 224.0.23.12, UDP port 3671).
    • It sends and receives KNX telegrams as multicast UDP packets.
    • This requires the network infrastructure to support multicast routing correctly.
    • The ESP32 directly processes the KNX cEMI (Common External Message Interface) frames within the UDP payload.

For most ESP32 applications, KNX IP Tunneling is a more straightforward approach when connecting to an existing KNX IP infrastructure.

Practical Examples

Let’s explore how to implement basic KNX communication with an ESP32.

Example 1: ESP32 with KNX TP via TPUART (Conceptual UART Communication)

This example will focus on the ESP32’s UART communication logic to send a command to a hypothetical TPUART module. Actually building and testing this requires a physical TPUART module (e.g., based on NCN5120 or similar), a KNX power supply, and another KNX device (or ETS with a bus monitor) to observe the telegrams.

Assumptions for this conceptual example:

  • We are communicating with a TPUART module that expects a simple serial frame.
  • The TPUART handles low-level KNX bus access and checksums.
  • ESP32 UART2 is used (TX: GPIO17, RX: GPIO16).
  • TPUART Baud Rate: 19200, 8 data bits, Even parity, 1 stop bit (common for some TPUARTs).

main/knx_tpuart_example.c:

C
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "esp_log.h"

#define TPUART_NUM UART_NUM_2
#define TPUART_TXD_PIN 17
#define TPUART_RXD_PIN 16
#define TPUART_RTS_PIN UART_PIN_NO_CHANGE // Or your actual RTS pin if used
#define TPUART_CTS_PIN UART_PIN_NO_CHANGE // Or your actual CTS pin if used

#define BUF_SIZE 1024

static const char *TAG = "KNX_TPUART";

// Simplified structure for sending a KNX command via TPUART
// This structure and its framing are HIGHLY dependent on the specific TPUART module used.
// This is a generic representation.
typedef struct {
    uint16_t group_address; // Target group address
    uint8_t data_len;       // Payload length (e.g., 1 for DPT 1.001)
    uint8_t payload[16];    // Max payload size
} knx_tp_command_t;

// Initialize UART for TPUART communication
void tpuart_init() {
    uart_config_t uart_config = {
        .baud_rate = 19200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_EVEN, // Common for TPUART
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    ESP_ERROR_CHECK(uart_driver_install(TPUART_NUM, BUF_SIZE * 2, 0, 0, NULL, 0));
    ESP_ERROR_CHECK(uart_param_config(TPUART_NUM, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(TPUART_NUM, TPUART_TXD_PIN, TPUART_RXD_PIN, TPUART_RTS_PIN, TPUART_CTS_PIN));
    ESP_LOGI(TAG, "TPUART initialized (TX: %d, RX: %d, Baud: %d 8E1)", TPUART_TXD_PIN, TPUART_RXD_PIN, uart_config.baud_rate);
}

// Send a command to the TPUART module
// Note: Actual framing (start bytes, checksums for serial link) depends on the TPUART module
void tpuart_send_knx_command(const knx_tp_command_t *cmd) {
    // Example: Send a "GroupValueWrite" for DPT 1.001 (On/Off)
    // The actual bytes sent to the TPUART will depend on its specific protocol.
    // This is a placeholder for the logic needed to frame the command for your TPUART.

    // Simplified representation:
    // Let's assume our TPUART expects:
    // <CMD_TYPE> <GA_HIGH> <GA_LOW> <LEN> <DATA_BYTE_0_WITH_APCI> ...
    // For GroupValueWrite (APCI = 0b10xxxxxx for write), DPT 1.001 (1 bit)
    // Data byte for ON: 0x81 (APCI for write + 1 bit data = 1)
    // Data byte for OFF: 0x80 (APCI for write + 1 bit data = 0)

    uint8_t tx_buffer[32];
    int tx_len = 0;

    // This is a conceptual framing. CONSULT YOUR TPUART DATASHEET.
    tx_buffer[tx_len++] = 0xBC; // Example: Some TPUARTs use a control byte or specific framing for commands
    tx_buffer[tx_len++] = (cmd->group_address >> 8) & 0xFF; // GA High
    tx_buffer[tx_len++] = cmd->group_address & 0xFF;        // GA Low
    tx_buffer[tx_len++] = cmd->data_len + 1; // Length of (APCI + Data). For 1 byte DPT, this would be 1.
                                          // For DPT 1.001, data_len is 1 (1 bit, but smallest unit is byte with APCI bits).
                                          // The APCI bits (e.g. for GroupValueWrite) are often combined with the first data byte.

    // For DPT 1.001, data_len would conceptually be 1 (for the single bit value).
    // The payload would contain the DPT value along with APCI bits.
    // Example: To send "ON" (value 1) to DPT 1.001 using GroupValueWrite
    // The actual payload byte might be 0b10000001 = 0x81 (assuming APCI for write is in upper bits)
    // For "OFF" (value 0), it might be 0b10000000 = 0x80
    memcpy(&tx_buffer[tx_len], cmd->payload, cmd->data_len);
    tx_len += cmd->data_len;

    // Add any other required framing bytes (e.g., checksum for UART link)
    // tx_buffer[tx_len++] = calculate_checksum(&tx_buffer[1], tx_len - 1); // Example

    int bytes_written = uart_write_bytes(TPUART_NUM, (const char *)tx_buffer, tx_len);
    if (bytes_written > 0) {
        ESP_LOGI(TAG, "Sent %d bytes to TPUART. GA: %04X, Payload[0]: %02X",
                 bytes_written, cmd->group_address, cmd->payload[0]);
    } else {
        ESP_LOGE(TAG, "Error sending data to TPUART");
    }
    // It's good practice to flush after writing, especially if not sending frequently
    uart_wait_tx_done(TPUART_NUM, pdMS_TO_TICKS(100));
}

// Task to listen for incoming data from TPUART
static void tpuart_rx_task(void *arg) {
    uint8_t *data = (uint8_t *) malloc(BUF_SIZE);
    while (1) {
        int len = uart_read_bytes(TPUART_NUM, data, BUF_SIZE -1 , pdMS_TO_TICKS(20)); // 20ms timeout
        if (len > 0) {
            data[len] = '\0'; // Null-terminate if expecting string-like data or for logging
            ESP_LOGI(TAG, "Received %d bytes from TPUART:", len);
            // Log as hex
            esp_log_buffer_hex(TAG, data, len);
            // Here you would parse the received TPUART frame according to its specification
            // to extract Source PA, Dest GA, DPT, and data.
            // Example: if (data[0] == KNX_TELEGRAM_INDICATOR_FROM_TPUART) { parse_knx_telegram(data, len); }
        }
        vTaskDelay(pdMS_TO_TICKS(10)); // Small delay to prevent busy-waiting if no data
    }
    free(data);
}


void app_main(void) {
    tpuart_init();
    xTaskCreate(tpuart_rx_task, "tpuart_rx_task", 2048, NULL, 10, NULL);

    vTaskDelay(pdMS_TO_TICKS(2000)); // Wait for UART to settle and RX task to start

    knx_tp_command_t cmd_light_on;
    cmd_light_on.group_address = 0x0101; // Example: Group Address 1/1/1
    cmd_light_on.data_len = 1;           // For DPT 1.001, payload is 1 byte (6 bits for APCI, 1 bit for data)
    cmd_light_on.payload[0] = 0x81;      // DPT 1.001 "ON" (0b10000001 -> APCI for write = 0b10xxxxxx, data bit = 1)

    ESP_LOGI(TAG, "Sending KNX command: Light ON to GA %04X", cmd_light_on.group_address);
    tpuart_send_knx_command(&cmd_light_on);

    vTaskDelay(pdMS_TO_TICKS(5000));

    knx_tp_command_t cmd_light_off;
    cmd_light_off.group_address = 0x0101; // Example: Group Address 1/1/1
    cmd_light_off.data_len = 1;
    cmd_light_off.payload[0] = 0x80;     // DPT 1.001 "OFF" (0b10000000 -> APCI for write = 0b10xxxxxx, data bit = 0)

    ESP_LOGI(TAG, "Sending KNX command: Light OFF to GA %04X", cmd_light_off.group_address);
    tpuart_send_knx_command(&cmd_light_off);
}

CMakeLists.txt (in main directory):

Ensure idf_component_register includes SRCS “knx_tpuart_example.c” and REQUIRES FREERTOS driver esp_log.

Hardware Required (for actual testing):

  • ESP32 Development Board.
  • A KNX TPUART module (e.g., a readily available module based on NCN5120, FZE1066, or Siemens TPUART2). Connect its UART RX/TX to ESP32’s GPIO16/GPIO17.
  • A KNX Bus Power Supply (typically 30V DC).
  • At least one KNX actuator (e.g., a switching actuator for a light) programmed via ETS to respond to Group Address 1/1/1 (0x0901 in the common 3-level scheme, or 0x0101 as used in code if 2-level is 0-indexed for main group). Ensure DPT 1.001 is configured for this GA.
  • KNX bus cable.

Warning: Connecting ESP32 GPIOs directly to a KNX bus will damage the ESP32 and will not work. A proper KNX transceiver (like a TPUART IC/module) is mandatory for KNX TP communication. The TPUART handles the electrical interface and part of the protocol.

Build Instructions:

  1. Open VS Code with the ESP-IDF extension.
  2. Create a new project or use an existing one.
  3. Place knx_tpuart_example.c in your main directory and update CMakeLists.txt.
  4. Configure your ESP32 target (e.g., idf.py set-target esp32).
  5. Build the project (idf.py build).

Run/Flash/Observe:

  1. Connect your ESP32 to your computer.
  2. Flash the firmware (idf.py -p /dev/ttyUSB0 flash or use the VS Code flash button. Replace /dev/ttyUSB0 with your ESP32’s serial port).
  3. Monitor the serial output (idf.py -p /dev/ttyUSB0 monitor or use the VS Code monitor button).
  4. Observation:
    • You should see log messages indicating UART initialization and the sending of commands.
    • If you have a real KNX setup with a TPUART module and an actuator configured for GA 1/1/1:
      • The actuator (e.g., light) should turn ON and then OFF.
      • The tpuart_rx_task would show any raw data received from the TPUART, which would be incoming KNX telegrams (e.g., acknowledgments or other bus traffic). You would need to implement parsing logic specific to your TPUART’s serial protocol to decode these.

Example 2: ESP32 as a KNX IP Client (Tunneling)

This example demonstrates connecting to a KNX IP Router/Interface and sending a GroupValueWrite telegram. This requires a KNX IP Router/Interface on your network.

Assumptions:

  • A KNX IP Router/Interface is available at 192.168.1.100 (replace with actual IP).
  • The KNX IP device supports KNXnet/IP Tunneling on UDP port 3671.
  • ESP32 connects to your Wi-Fi network.

main/knx_ip_client_example.c:

C
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>

// Wi-Fi Configuration
#define WIFI_SSID      "YOUR_WIFI_SSID"
#define WIFI_PASS      "YOUR_WIFI_PASSWORD"
#define MAX_WIFI_RETRY 10

// KNX IP Configuration
#define KNX_IP_ROUTER_ADDR "192.168.1.100" // Replace with your KNX IP Router/Interface IP
#define KNX_IP_PORT        3671

static const char *TAG = "KNX_IP";
static EventGroupHandle_t s_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1
static int s_retry_num = 0;

// KNXnet/IP Core Service Identifiers
#define KNXNETIP_CORE_SEARCH_REQUEST        0x0201
#define KNXNETIP_CORE_SEARCH_RESPONSE       0x0202
#define KNXNETIP_CORE_DESCRIPTION_REQUEST   0x0203
#define KNXNETIP_CORE_DESCRIPTION_RESPONSE  0x0204
#define KNXNETIP_CORE_CONNECT_REQUEST       0x0205
#define KNXNETIP_CORE_CONNECT_RESPONSE      0x0206
#define KNXNETIP_CORE_CONNECTIONSTATE_REQUEST 0x0207
#define KNXNETIP_CORE_CONNECTIONSTATE_RESPONSE 0x0208
#define KNXNETIP_CORE_DISCONNECT_REQUEST    0x0209
#define KNXNETIP_CORE_DISCONNECT_RESPONSE   0x020A

// KNXnet/IP Tunneling Service Identifiers
#define KNXNETIP_TUNNEL_TUNNELLING_REQUEST  0x0420
#define KNXNETIP_TUNNEL_TUNNELLING_ACK      0x0421

// KNXnet/IP Header Structure (simplified)
typedef struct {
    uint8_t header_len;         // 0x06 for core services
    uint8_t protocol_version;   // 0x10 (KNXnet/IP v1.0)
    uint16_t service_type_id;   // e.g., CONNECT_REQUEST
    uint16_t total_len;         // Total length of the frame (header + body)
} knx_ip_header_t;

// KNXnet/IP Connect Request Body (Control Endpoint + Data Endpoint)
typedef struct {
    uint8_t structure_len_hpai_ctrl; // 0x08
    uint8_t host_protocol_code_ctrl; // 0x01 (UDP)
    uint8_t ip_addr_ctrl[4];         // 0.0.0.0 (don't care for control endpoint on client)
    uint16_t port_ctrl;              // 0 (don't care for control endpoint on client)
    uint8_t structure_len_hpai_data; // 0x08
    uint8_t host_protocol_code_data; // 0x01 (UDP)
    uint8_t ip_addr_data[4];         // 0.0.0.0 (don't care for data endpoint on client)
    uint16_t port_data;              // 0 (don't care for data endpoint on client)
    uint8_t structure_len_cri;       // 0x04
    uint8_t connection_type_code;    // 0x04 (Tunneling Connection)
    uint8_t knx_layer;               // 0x02 (Tunneling on bus link layer)
    uint8_t reserved;                // 0x00
} knx_ip_connect_request_body_t;

// KNXnet/IP Connect Response (simplified)
typedef struct {
    uint8_t channel_id;
    uint8_t status;
    // ... other fields (HPAI, CRI data)
} knx_ip_connect_response_data_t; // This is a partial representation

// KNX cEMI frame for Tunneling Request (L_Data.req)
// This is a simplified version for GroupValueWrite, DPT 1.001
typedef struct {
    uint8_t message_code;       // 0x11 for L_Data.req
    uint8_t add_info_len;       // 0x00
    uint8_t control_field_1;    // e.g., 0xBC (standard frame, normal prio, repeat ok)
    uint8_t control_field_2;    // e.g., 0xE0 (destination is group addr, hop count 6)
    uint16_t source_addr;       // 0x0000 (KNX IP router will fill this, or a configured PA)
    uint16_t dest_group_addr;   // Target Group Address
    uint8_t data_len_apci;      // Length of APCI + PDU (e.g., 1 for DPT 1.001)
    uint8_t apci_data;          // APCI (e.g., 0x80 for GroupValueWrite) + Data (e.g., 0/1)
} knx_cemi_l_data_req_t;


// KNXnet/IP Tunneling Request Header (prepended to cEMI)
typedef struct {
    uint8_t structure_len;      // 0x04
    uint8_t channel_id;
    uint8_t seq_counter;
    uint8_t reserved;           // 0x00
} knx_tunnel_header_t;


static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                               int32_t event_id, void* event_data) {
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        if (s_retry_num < MAX_WIFI_RETRY) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "Retrying to connect to the AP");
        } else {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"Connect to the AP fail");
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void wifi_init_sta(void) {
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
            .threshold.authmode = WIFI_AUTH_WPA2_PSK, // Adjust as needed
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "Connected to AP SSID:%s", WIFI_SSID);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s", WIFI_SSID);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }
}

static void knx_ip_task(void *pvParameters) {
    char rx_buffer[128];
    char tx_buffer[128];
    int sock = -1;
    uint8_t knx_channel_id = 0;
    uint8_t knx_seq_counter = 0; // Must be incremented for each Tunneling Request


    // Wait for Wi-Fi connection
    xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
    ESP_LOGI(TAG, "Wi-Fi Connected. Starting KNX IP Task.");

    struct sockaddr_in dest_addr;
    dest_addr.sin_addr.s_addr = inet_addr(KNX_IP_ROUTER_ADDR);
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(KNX_IP_PORT);

    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        goto knx_fail;
    }
    ESP_LOGI(TAG, "Socket created, connecting to %s:%d", KNX_IP_ROUTER_ADDR, KNX_IP_PORT);

    // 1. Send CONNECT_REQUEST
    knx_ip_header_t *hdr = (knx_ip_header_t *)tx_buffer;
    hdr->header_len = 0x06;
    hdr->protocol_version = 0x10;
    hdr->service_type_id = htons(KNXNETIP_CORE_CONNECT_REQUEST); // Big Endian

    knx_ip_connect_request_body_t *connect_req_body = (knx_ip_connect_request_body_t *)(tx_buffer + sizeof(knx_ip_header_t));
    // Control Endpoint HPAI (client can leave it 0.0.0.0:0)
    connect_req_body->structure_len_hpai_ctrl = 0x08;
    connect_req_body->host_protocol_code_ctrl = 0x01; // UDP
    memset(connect_req_body->ip_addr_ctrl, 0, 4);
    connect_req_body->port_ctrl = 0;
    // Data Endpoint HPAI (client can leave it 0.0.0.0:0)
    connect_req_body->structure_len_hpai_data = 0x08;
    connect_req_body->host_protocol_code_data = 0x01; // UDP
    memset(connect_req_body->ip_addr_data, 0, 4);
    connect_req_body->port_data = 0;
    // Connection Request Information (CRI)
    connect_req_body->structure_len_cri = 0x04;
    connect_req_body->connection_type_code = 0x04; // Tunneling
    connect_req_body->knx_layer = 0x02; // Busmon not supported, use Link Layer tunneling
    connect_req_body->reserved = 0x00;

    hdr->total_len = htons(sizeof(knx_ip_header_t) + sizeof(knx_ip_connect_request_body_t));

    int err = sendto(sock, tx_buffer, sizeof(knx_ip_header_t) + sizeof(knx_ip_connect_request_body_t), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err < 0) {
        ESP_LOGE(TAG, "Error occurred during sending CONNECT_REQUEST: errno %d", errno);
        goto knx_fail;
    }
    ESP_LOGI(TAG, "CONNECT_REQUEST sent");

    // 2. Receive CONNECT_RESPONSE
    struct sockaddr_storage source_addr;
    socklen_t socklen = sizeof(source_addr);
    int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);

    if (len < 0) {
        ESP_LOGE(TAG, "recvfrom failed for CONNECT_RESPONSE: errno %d", errno);
        goto knx_fail;
    } else {
        ESP_LOGI(TAG, "Received %d bytes for CONNECT_RESPONSE", len);
        knx_ip_header_t *resp_hdr = (knx_ip_header_t *)rx_buffer;
        if (ntohs(resp_hdr->service_type_id) == KNXNETIP_CORE_CONNECT_RESPONSE) {
            knx_ip_connect_response_data_t *resp_data = (knx_ip_connect_response_data_t *)(rx_buffer + sizeof(knx_ip_header_t));
            if (resp_data->status == 0x00) { // E_NO_ERROR
                knx_channel_id = resp_data->channel_id;
                ESP_LOGI(TAG, "KNX IP Connection successful. Channel ID: %d", knx_channel_id);

                // Now we have a channel, let's send a TUNNELLING_REQUEST (e.g., GroupValueWrite)
                vTaskDelay(pdMS_TO_TICKS(1000)); // Wait a bit

                knx_ip_header_t *tunnel_hdr = (knx_ip_header_t *)tx_buffer;
                tunnel_hdr->header_len = 0x06;
                tunnel_hdr->protocol_version = 0x10;
                tunnel_hdr->service_type_id = htons(KNXNETIP_TUNNEL_TUNNELLING_REQUEST);

                knx_tunnel_header_t *tunnel_sub_hdr = (knx_tunnel_header_t *)(tx_buffer + sizeof(knx_ip_header_t));
                tunnel_sub_hdr->structure_len = 0x04;
                tunnel_sub_hdr->channel_id = knx_channel_id;
                tunnel_sub_hdr->seq_counter = knx_seq_counter++; // Use and increment
                tunnel_sub_hdr->reserved = 0x00;

                knx_cemi_l_data_req_t *cemi = (knx_cemi_l_data_req_t *)(tx_buffer + sizeof(knx_ip_header_t) + sizeof(knx_tunnel_header_t));
                cemi->message_code = 0x11; // L_Data.req
                cemi->add_info_len = 0x00;
                cemi->control_field_1 = 0xBC; // Standard frame, priority system, ACK request
                cemi->control_field_2 = 0xE0; // Dest is Group Addr, Hop Count 6 (standard)
                cemi->source_addr = htons(0x0000); // Or your device's PA if configured & known
                cemi->dest_group_addr = htons(0x0101); // Target GA 1/1/1 (format: Main(5bit)/Sub(11bit) -> 00001 00000010001 = 0x0901 or use 2-level: 0001 00000001 -> 0x0101 )
                                                     // For 3-level 1/1/1: (1<<11) | (1<<8) | 1 = 0x0901
                                                     // For 2-level Main/Sub (as used in example 1, 0x0101): if main_group=1, sub_group=1 -> (1<<8)|1 = 257 = 0x0101.
                                                     // Ensure format matches ETS project! Assuming 0x0101 means Main=1, Sub=1 (using 2-level interpretation here for simplicity)

                cemi->data_len_apci = 1;    // Length of (APCI + Data payload). For DPT 1.001, 1 byte.
                cemi->apci_data = 0x81;     // GroupValueWrite (APCI bits 10xx xxxx) + Data 1 (ON)
                                            // Lower 6 bits of APCI for DPT 1.001 are 000000.
                                            // APCI Command: 0b10 (GroupValueWrite) -> shifted (value << 2) becomes 0b10000000 = 0x80
                                            // Data: 0x01 (ON) -> combined 0x80 | 0x01 = 0x81

                uint16_t total_tunnel_len = sizeof(knx_ip_header_t) + sizeof(knx_tunnel_header_t) + sizeof(knx_cemi_l_data_req_t);
                tunnel_hdr->total_len = htons(total_tunnel_len);

                err = sendto(sock, tx_buffer, total_tunnel_len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
                if (err < 0) {
                    ESP_LOGE(TAG, "Error occurred during sending TUNNELLING_REQUEST: errno %d", errno);
                } else {
                    ESP_LOGI(TAG, "TUNNELLING_REQUEST sent (Light ON to GA %04X). Seq: %d", ntohs(cemi->dest_group_addr), tunnel_sub_hdr->seq_counter -1);

                    // Wait for TUNNELLING_ACK
                    len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
                    if (len > 0) {
                        knx_ip_header_t *ack_hdr = (knx_ip_header_t *)rx_buffer;
                        if (ntohs(ack_hdr->service_type_id) == KNXNETIP_TUNNEL_TUNNELLING_ACK) {
                           knx_tunnel_header_t *ack_tunnel_hdr = (knx_tunnel_header_t *)(rx_buffer + sizeof(knx_ip_header_t));
                           // Check ack_tunnel_hdr->channel_id and ack_tunnel_hdr->seq_counter
                           // And status byte which is rx_buffer[sizeof(knx_ip_header_t) + sizeof(knx_tunnel_header_t)]
                           uint8_t ack_status = rx_buffer[sizeof(knx_ip_header_t) + sizeof(knx_tunnel_header_t)];
                           ESP_LOGI(TAG, "TUNNELLING_ACK received. Seq: %d, Status: %02X", ack_tunnel_hdr->seq_counter, ack_status);
                        } else {
                           ESP_LOGW(TAG, "Received unexpected KNX IP packet instead of ACK. Service: 0x%04X", ntohs(ack_hdr->service_type_id));
                        }
                    } else {
                        ESP_LOGE(TAG, "Error receiving TUNNELLING_ACK");
                    }
                }
                // Implement heartbeat (CONNECTIONSTATE_REQUEST) and disconnect logic here for robust app
            } else {
                ESP_LOGE(TAG, "KNX IP Connection failed. Status: %02X", resp_data->status);
            }
        } else {
            ESP_LOGE(TAG, "Received unexpected KNX IP packet. Service Type: 0x%04X", ntohs(resp_hdr->service_type_id));
        }
    }

knx_fail:
    if (sock != -1) {
        // Send DISCONNECT_REQUEST (important for releasing resources on the KNX IP router)
        // Similar structure to CONNECT_REQUEST but with DISCONNECT_REQUEST service type
        // and using the obtained knx_channel_id.
        ESP_LOGI(TAG, "Shutting down socket and cleaning up...");
        shutdown(sock, 0);
        close(sock);
    }
    vTaskDelete(NULL);
}

void app_main(void) {
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();

    xTaskCreate(knx_ip_task, "knx_ip_task", 4096, NULL, 5, NULL);
}

CMakeLists.txt (in main directory):

Ensure idf_component_register includes SRCS “knx_ip_client_example.c” and REQUIRES FREERTOS esp_wifi esp_event esp_netif nvs_flash lwip esp_log.

You might also need to add PRIV_REQUIRES lwip if not automatically picked up.

sdkconfig.defaults (or use idf.py menuconfig):

Plaintext
CONFIG_ESP_WIFI_SSID="YOUR_WIFI_SSID"
CONFIG_ESP_WIFI_PASSWORD="YOUR_WIFI_PASSWORD"
CONFIG_ESP_MAXIMUM_RETRY=10
# Ensure LWIP options are enabled (usually by default)
# Ensure TCP/IP Adapter is enabled

Replace "YOUR_WIFI_SSID" and "YOUR_WIFI_PASSWORD" with your actual Wi-Fi credentials. You can also set these via idf.py menuconfig under “Example Connection Configuration”.

Build Instructions:

  1. Save the code and update CMakeLists.txt.
  2. Configure Wi-Fi SSID and Password (either in code, sdkconfig.defaults, or via menuconfig).
  3. idf.py set-target esp32 (or your variant).
  4. idf.py build.

Run/Flash/Observe:

  1. Flash the firmware: idf.py -p /dev/ttyUSB0 flash.
  2. Monitor the serial output: idf.py -p /dev/ttyUSB0 monitor.
  3. Prerequisites:
    • A KNX IP Router or Interface must be running on your network at the IP address specified (KNX_IP_ROUTER_ADDR).
    • This KNX IP device must be connected to a powered KNX TP bus.
    • A KNX device (e.g., light actuator) should be programmed via ETS to respond to Group Address 1/1/1 (or the GA you use, e.g. 0x0101 or 0x0901 depending on ETS scheme) with DPT 1.001.
  4. Observation:
    • The ESP32 will connect to Wi-Fi.
    • It will then attempt to send a KNXnet/IP CONNECT_REQUEST to the specified KNX IP router.
    • If successful, it will receive a CONNECT_RESPONSE with a channel ID.
    • It will then send a TUNNELLING_REQUEST containing a cEMI message to turn ON the light at GA 1/1/1.
    • The light on the KNX bus should turn ON.
    • The ESP32 should receive a TUNNELLING_ACK.
    • Log messages will show the progress.

Tip: KNXnet/IP is a complex protocol. The example above implements only a minimal part for sending one command. A robust application would need to handle sequence counters correctly, heartbeats (CONNECTIONSTATE_REQUEST/RESPONSE) to keep the tunnel open, DISCONNECT_REQUESTs, error handling, and parsing incoming Tunneling Requests from the KNX bus. Consider using existing KNXnet/IP libraries for more complex projects if available and suitable for ESP-IDF.

Variant Notes

The KNX integration capabilities are generally similar across ESP32 variants, but some nuances exist:

Variant KNX TP (via TPUART) KNX IP (via Wi-Fi) Key Consideration
ESP32 (Classic) Excellent (3 UARTs) Excellent (Wi-Fi & Ethernet MAC) A powerful, all-around choice for any KNX project.
ESP32-S2 / S3 Excellent (3 UARTs) Excellent (Wi-Fi) S3 offers AI acceleration and BLE 5. Both have native USB. Great for complex gateways.
ESP32-C3 Good (2 UARTs) Good (Wi-Fi) Cost-effective RISC-V option for simpler KNX IP clients or basic TPUART devices.
ESP32-C6 Excellent (3 UARTs) Excellent (Wi-Fi 6) Future-proof with Wi-Fi 6 and 802.15.4 radio (Thread/Zigbee) for potential multi-protocol gateways.
ESP32-H2 Excellent (3 UARTs) No Wi-Fi Best for TPUART-only devices or KNX IP via an external Ethernet PHY. 802.15.4 radio is a key feature.

General Considerations for all variants:

  • UART Availability: For TPUART solutions, ensure you select a variant with enough available UART ports if other peripherals also require UARTs.
  • Memory: KNX IP stacks, especially if handling many connections or complex logic, can consume significant RAM and Flash. Choose a variant with adequate memory (e.g., ESP32 with 4MB+ Flash and PSRAM if needed for very complex apps).
  • Peripherals: If your application requires other specific peripherals (I2C, SPI, ADC) in addition to KNX, verify their availability on the chosen variant alongside the UART/network interfaces.
  • Power Consumption: For battery-powered KNX devices (rare for TP/IP, more common for RF concepts), the low-power modes and overall consumption of the chosen variant become critical.

The examples provided should work on these variants with appropriate pin configurations for UART if using the TPUART example, and network configuration for the KNX IP example (for Wi-Fi enabled variants).

Common Mistakes & Troubleshooting Tips

Mistake / Issue Symptom(s) Troubleshooting / Solution
Address Mismatch ESP32 sends a command, but the KNX actuator doesn’t respond. No activity seen in ETS group monitor for the intended Group Address (GA). Verify GA in code: Ensure 0x0101 in the C code exactly matches the GA configured in ETS (e.g., 1/1/1). Remember that 3-level addresses like 1/2/3 have a different bitwise representation than 2-level ones. Double-check DPT assignments match in both code and ETS.
TPUART Communication Error ESP32 logs show data sent, but nothing appears on the KNX bus. Or, gibberish is received in the ESP32 log from the TPUART. Check UART Config: Confirm baud rate, parity, and stop bits match the TPUART’s datasheet (e.g., 19200, 8E1 is common). Check Wiring: Ensure ESP32 TX connects to TPUART RX, and ESP32 RX to TPUART TX. Check Power: The KNX bus must be powered by a KNX PSU (~30V).
KNX IP Connection Fails ESP32 fails to get a CONNECT_RESPONSE. Log shows timeout or “connection refused”. Verify Network: Check the KNX IP Router’s IP address and ensure the ESP32 is on the same Wi-Fi network and subnet. Check Firewall: Ensure no firewall on your router or PC is blocking UDP port 3671. Check Tunnels: Ensure the KNX IP Router has free tunneling connections available.
Incorrect DPT Encoding A telegram is sent to the correct GA, but the device behaves unexpectedly (e.g., a light doesn’t dim correctly, a temperature display shows a weird value). Consult DPT Spec: Refer to the official KNX specification for the exact bit/byte format. For DPT 9.001 (temperature), ensure you are correctly converting your float to the 2-byte signed float format. For DPT 1.001 (switch), confirm your “ON” payload includes the correct APCI bits for a GroupValueWrite.

Exercises

  1. TPUART – Read KNX Sensor:Modify the TPUART example (assuming you have a physical TPUART module and a KNX push button or sensor). Configure the ESP32 to listen for incoming KNX telegrams on a specific Group Address associated with a KNX push button (e.g., GA 2/0/1, DPT 1.001). When the button is pressed/released, the ESP32 should parse the incoming message from the TPUART and print the received value (ON/OFF state) to the serial monitor.
  2. KNX IP – Read Group Address:Extend the KNX IP client example. Implement the logic to send a GroupValueRead request to a specific KNX Group Address (e.g., GA 1/2/0, DPT 9.001 for temperature). Parse the incoming GroupValueResponse (which will arrive as a Tunneling Request from the KNX IP router) and display the received temperature value on the serial monitor. Do this periodically (e.g., every 30 seconds).
  3. KNX IP – Basic Control Logic:Create an application using the KNX IP example.
    • Define two Group Addresses: GA_MOTION (e.g., 2/1/0, DPT 1.002 Motion Detected) and GA_LIGHT (e.g., 1/1/0, DPT 1.001 On/Off).
    • The ESP32 should listen for messages on GA_MOTION.
    • If a “motion detected” (Value TRUE) message is received on GA_MOTION, the ESP32 should send an “ON” command to GA_LIGHT.
    • After sending “ON”, start a 1-minute timer (use FreeRTOS timers or vTaskDelay). If no new “motion detected” message is received within that minute, send an “OFF” command to GA_LIGHT. If new motion is detected, reset the timer.
  4. DPT Encoding/Decoding Functions:Write C functions for the ESP32 to:
    • Encode:
      • Take a boolean value (true/false) and return the uint8_t payload byte for DPT 1.001 (Switch) GroupValueWrite (including APCI bits for write).
      • Take a float value (e.g., 22.5 °C) and return the two uint8_t payload bytes for DPT 9.001 (Temperature) GroupValueWrite. (Refer to DPT 9.001 spec for float encoding: 2-byte signed float, value = 0.01 * (exponent * mantissa)).
    • Decode:
      • Take the payload byte(s) from a received KNX telegram for DPT 1.001 and return a boolean.
      • Take the payload byte(s) for DPT 9.001 and return a float temperature value.Test these functions by printing their output for sample inputs.

Summary

  • KNX is a robust, decentralized, and standardized (ISO/IEC 14543-3) protocol for building automation, ensuring interoperability between devices from various manufacturers.
  • It supports multiple communication media: Twisted Pair (TP, most common), Power Line (PL), Radio Frequency (RF), and IP (Ethernet/Wi-Fi).
  • KNX communication relies on telegrams, which are addressed using Physical Addresses (unique device ID) or Group Addresses (functional communication).
  • Data Point Types (DPTs) define the format and meaning of data within telegrams, crucial for interoperability.
  • ESP32 can interface with KNX TP networks using a TPUART module/IC, communicating via its UART peripheral. This requires careful handling of the TPUART’s specific serial protocol.
  • ESP32 can also integrate into KNX IP networks (typically as a Tunneling client) using its Wi-Fi or Ethernet capabilities, by implementing parts of the KNXnet/IP protocol over UDP.
  • Successful KNX integration requires careful configuration of addresses, DPTs, and understanding the specifics of the chosen interface method (TPUART or KNX IP).
  • Various ESP32 variants are suitable, with considerations for UART availability, network interfaces, and memory depending on the complexity of the KNX application.

Further Reading

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top