Chapter 217: EnOcean Energy Harvesting Protocol

Chapter Objectives

By the end of this chapter, you will be able to:

  • Explain the concept of energy harvesting for wireless devices in building automation.
  • Describe the fundamentals of the EnOcean wireless protocol, including its operating frequencies and telegram structure.
  • Understand the role and importance of EnOcean Equipment Profiles (EEPs) for interoperability.
  • Recognize the need for an external EnOcean transceiver module (TCM) for use with an ESP32.
  • Implement an ESP32-based gateway to receive and parse EnOcean telegrams via a UART-connected TCM.
  • Develop a practical application that uses a batteryless switch to control a device connected to the ESP32.
  • Troubleshoot common issues in an ESP32-EnOcean gateway setup.

Introduction

In the world of building automation, running wires for every switch, sensor, and actuator can be incredibly expensive and inflexible. While battery-powered wireless devices offer a solution, they introduce a significant maintenance burden: batteries need to be regularly monitored and replaced, a daunting task in a building with thousands of nodes. What if we could eliminate both the wires and the batteries?

This is the promise of the EnOcean protocol. It is a specialized wireless technology designed for ultra-low-power devices that “harvest” the energy they need from their immediate environment. A press of a switch, a small amount of indoor light, or a slight temperature difference is enough to power a sensor and transmit a wireless signal. This creates a world of “fit-and-forget” devices that can be placed anywhere without concern for power infrastructure.

In this chapter, we will explore the EnOcean protocol and learn how to build an ESP32-based gateway. This gateway will bridge the world of energy-harvesting EnOcean devices with the powerful connectivity and logic capabilities of the ESP32, enabling truly smart, sustainable, and maintenance-free automation systems.

Theory

What is EnOcean?

EnOcean is a wireless communication protocol and technology platform optimized for battery-free, energy-harvesting devices. The core principle is to use minuscule amounts of energy captured from the environment to power a sensor or switch long enough to transmit a small data packet, known as a telegram.

Energy Harvesting Sources

The magic of EnOcean lies in its ability to convert ambient energy into usable electrical power. The three primary methods are:

  • Kinetic (Electromechanical): This is the most common source for switches. The physical act of pressing a rocker switch moves a magnetic core through a small coil, inducing a current. This tiny pulse of energy is sufficient to transmit several redundant telegrams, ensuring reliability.
  • Solar (Photovoltaic): Small, efficient solar cells, optimized for low-level indoor lighting, are used to power sensors like those for temperature, humidity, and occupancy. They often include a small storage element to operate for periods of darkness.
  • Thermal (Thermoelectric): Using the Peltier effect, a thermoelectric generator can produce power from a temperature difference between two surfaces. This is ideal for applications like monitoring radiator valve temperatures.

The EnOcean Wireless Protocol

To operate on such little energy, the wireless protocol must be incredibly efficient.

  • Frequency: EnOcean operates in the sub-GHz ISM (Industrial, Scientific, and Medical) bands, which provide excellent range and building penetration. The primary frequencies are 868 MHz in Europe, 902 MHz in North America, and 928 MHz in Japan.
  • Modulation: It typically uses Amplitude-Shift Keying (ASK) or Frequency-Shift Keying (FSK).
  • Telegrams: Instead of maintaining a constant connection, EnOcean devices wake up, transmit a very short telegram (around 1ms), and go back to sleep. To ensure the message is received, the telegram is usually transmitted three times in quick succession within a pseudo-random interval.

EnOcean Telegrams and EEPs

For devices from different manufacturers to understand each other, they need a common language. EnOcean achieves this through EnOcean Equipment Profiles (EEPs). An EEP defines how the data within a telegram should be interpreted.

A raw EnOcean telegram contains:

Field Abbreviation Size Description
Radio Organization RORG 1 Byte Defines the fundamental telegram type and structure. This is the first key to understanding the message. Ex: 0xF6 for RPS (Rocker Push Switch).
Data 1-14 Bytes The actual payload of the message. Its meaning is defined by the EnOcean Equipment Profile (EEP), which is linked to the RORG.
Originator ID / Source ID 4 Bytes A unique 32-bit identifier for the transmitting device. This is how you know which switch or sensor sent the message.
Status 1 Byte Contains information about the transmission, such as signal strength (RSSI) and whether redundant telegrams are being sent.

The EEP provides the key to unlock the Data field. For example, EEP F6-02-01 corresponds to a simple rocker switch. The standard specifies exactly which bits in the data payload correspond to “Button A Pressed,” “Button B Released,” etc. When our ESP32 receives a telegram, it must first identify the RORG and then use the corresponding EEP to make sense of the data.

The Transceiver Module (TCM)

The ESP32’s built-in radios (Wi-Fi, Bluetooth) do not operate in the EnOcean frequency bands and do not natively speak the EnOcean protocol. Therefore, we cannot interact with EnOcean devices directly. Instead, we use a dedicated EnOcean Transceiver Module (TCM), such as the popular EnOcean TCM 310 or TCM 515.

This module acts as a bridge. It handles all the low-level radio communication and protocol details. It receives EnOcean telegrams from the air and forwards them to our ESP32 over a standard UART serial connection. To make this communication standardized, the TCM wraps the raw radio telegrams in a simple serial protocol called the EnOcean Serial Protocol (ESP3). Our job is to parse these ESP3 frames coming from the TCM.

Practical Example: EnOcean Switch Gateway

Let’s build a gateway that listens for a press from an EnOcean PTM 210 batteryless rocker switch and uses it to toggle the onboard LED of an ESP32 board.

Hardware Requirements

  1. ESP32 Development Board: Any variant will work.
  2. EnOcean Transceiver Module: An EnOcean gateway device that can output ESP3 frames over UART. A common choice is a USB stick like the EnOcean USB 300, or a bare module like the TCM 310. If using a USB stick, you may need a USB-to-TTL adapter to easily access its UART pins.
  3. EnOcean PTM 210 Switch: A standard batteryless rocker switch.
  4. Wiring: Jumper wires to connect the ESP32 and TCM module.

Wiring (ESP32 to TCM Module):

  • ESP32 GND -> TCM GND
  • ESP32 3.3V -> TCM VCC
  • ESP32 TX Pin (e.g., GPIO 17) -> TCM RX Pin
  • ESP32 RX Pin (e.g., GPIO 16) -> TCM TX Pin

Warning: Always check the datasheet for your specific TCM module. Voltages and pinouts can vary. The EnOcean USB 300 stick, for example, is a 5V device, but its logic levels are typically 3.3V tolerant.

Project Setup in VS Code

  1. Create a new, standard ESP-IDF project.
  2. No special component dependencies are needed in main/CMakeLists.txt beyond the default drivers. We will use the standard driver/uart component.
  3. Copy the code below into your main.c file.

EnOcean Gateway Code

This code sets up a UART interface to listen for ESP3 frames from the TCM. It includes functions to parse these frames and decode a specific EEP for a rocker switch.

flowchart TD
    subgraph UART Task
        A[Start UART Read] --> B{Bytes received?};
        B -->|No| A;
        B -->|Yes| C[Loop through each byte];
        C --> D{"Byte == 0x55<br>(Sync Byte)?"};
        D -->|No| C;
        D -->|Yes| E(Sync Byte Found!):::start;
        E --> F{"Enough bytes for<br>full header? (5+ bytes)"};
        F -->|No| G([End Loop / Wait for More Data]):::check;
        F -->|Yes| H[Read Header:<br>Data Length, Optional Length, Packet Type];
        H --> I{"Packet Type == 0x01<br>(RADIO_ERP1)?"}:::decision;
        I -->|"No<br>(e.g., Response Packet)"| C;
        I -->|Yes| J{"Enough bytes for<br>full packet?<br>(Header + Payload + Checksum)"};
        J -->|No| G;
        J -->|Yes| K["Extract Payload (ERP1 Telegram)"]:::process;
        K --> L("<b>parse_erp1_telegram()</b>"):::process;
        L --> M[Check RORG & Decode EEP];
        M --> N("Perform Action<br>e.g., Toggle LED"):::endo;
        N --> C;
    end

    classDef start fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6;
    classDef endo fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46;
    classDef decision fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E;
    classDef process fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF;
    classDef check fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B;

C
/*
 * Chapter 217: EnOcean Energy Harvesting Protocol Gateway
 *
 * This example demonstrates how to create an ESP32 gateway that listens for
 * telegrams from an EnOcean device via a UART-connected TCM module.
 * It is configured to listen for a PTM 210 rocker switch (EEP F6-02-01).
 *
 * Hardware:
 * - ESP32
 * - EnOcean Transceiver Module (e.g., TCM 310, USB 300)
 * - EnOcean PTM 210 Rocker Switch
 *
 * Connections (ESP32 -> TCM):
 * - GND -> GND
 * - 3.3V -> VCC
 * - GPIO17 (TX) -> RX
 * - GPIO16 (RX) -> TX
 */

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

static const char *TAG = "ENOCEAN_GATEWAY";

// --- UART Configuration ---
#define UART_NUM            (UART_NUM_2)
#define UART_TX_PIN         (GPIO_NUM_17)
#define UART_RX_PIN         (GPIO_NUM_16)
#define UART_BUF_SIZE       (1024)

// --- GPIO Configuration ---
#define BLINK_GPIO          (GPIO_NUM_2) // Onboard LED on many DevKits

// --- EnOcean ESP3 Packet Constants ---
#define ESP3_SYNC_BYTE      0x55
#define RADIO_ERP1          0x01 // Packet Type for Radio Telegram

// --- EEP F6-02-01 (Rocker Switch) Constants ---
#define RORG_RPS            0xF6
#define FUNC_RPS            0x02
#define TYPE_RPS            0x01

static void configure_led(void) {
    gpio_reset_pin(BLINK_GPIO);
    gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
}

// Function to parse an incoming ESP3 radio telegram
static void parse_erp1_telegram(uint8_t *payload, uint16_t length) {
    if (length < 7) { // Minimum length for RPS telegram + originator ID + status
        ESP_LOGW(TAG, "ERP1 packet too short");
        return;
    }

    uint8_t rorg = payload[0];
    
    // Check if it's an RPS (Rocker Push Switch) telegram
    if (rorg == RORG_RPS) {
        uint8_t data = payload[1];
        uint32_t originator_id = (payload[2] << 24) | (payload[3] << 16) | (payload[4] << 8) | payload[5];
        uint8_t status = payload[6];

        ESP_LOGI(TAG, "RPS Telegram Received!");
        ESP_LOGI(TAG, "  - Originator ID: 0x%08x", originator_id);
        ESP_LOGI(TAG, "  - Data Byte: 0x%02x", data);
        ESP_LOGI(TAG, "  - Status Byte: 0x%02x", status);

        // For EEP F6-02-01, bit 0 indicates button state (0=pressed, 1=released)
        bool pressed = !(data & 0x01); 

        if (pressed) {
            ESP_LOGI(TAG, "Button pressed! Toggling LED.");
            // Toggle LED
            int current_level = gpio_get_level(BLINK_GPIO);
            gpio_set_level(BLINK_GPIO, !current_level);
        } else {
            ESP_LOGI(TAG, "Button released.");
        }
    }
}

// UART task to read data from the EnOcean TCM
static void uart_task(void *pvParameters) {
    uint8_t *data = (uint8_t *) malloc(UART_BUF_SIZE);

    while (1) {
        int len = uart_read_bytes(UART_NUM, data, UART_BUF_SIZE, 20 / portTICK_PERIOD_MS);
        if (len > 0) {
            // Look for the ESP3 sync byte
            for (int i = 0; i < len; ++i) {
                if (data[i] == ESP3_SYNC_BYTE) {
                    // Sync byte found, check header
                    if (len - i >= 5) { // Check if we have the full header
                        uint16_t data_length = (data[i+1] << 8) | data[i+2];
                        uint8_t optional_length = data[i+3];
                        uint8_t packet_type = data[i+4];

                        // We only care about ERP1 radio telegrams
                        if (packet_type == RADIO_ERP1) {
                            uint16_t total_length = 5 + data_length + optional_length + 1; // Header + data + optional + checksum
                            if (len - i >= total_length) {
                                // We have a full packet
                                uint8_t *payload = &data[i+5];
                                // TODO: Add checksum verification here for production code
                                parse_erp1_telegram(payload, data_length);
                                i += total_length - 1; // Move index past this packet
                            }
                        }
                    }
                }
            }
        }
    }
    free(data);
}

void app_main(void) {
    configure_led();

    // 1. Configure UART
    uart_config_t uart_config = {
        .baud_rate = 57600, // Standard baud rate for many TCMs
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    ESP_ERROR_CHECK(uart_driver_install(UART_NUM, UART_BUF_SIZE * 2, 0, 0, NULL, 0));
    ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));

    ESP_LOGI(TAG, "EnOcean Gateway Initialized. Waiting for telegrams...");

    // 2. Create a task to handle UART events
    xTaskCreate(uart_task, "uart_task", 4096, NULL, 10, NULL);
}

Build, Flash, and Run

  1. Connect Hardware: Wire the ESP32 to your EnOcean TCM module. Power both devices.
  2. Build: Click the Build button in VS Code.
  3. Flash: Connect the ESP32 to your computer and click the Flash button.
  4. Monitor: Open the serial monitor.

Expected Output

When you press one of the rocker buttons on your PTM 210 switch, you should see log output similar to this, and the onboard LED on your ESP32 board will toggle.

Plaintext
I (350) ENOCEAN_GATEWAY: EnOcean Gateway Initialized. Waiting for telegrams...
I (12540) ENOCEAN_GATEWAY: RPS Telegram Received!
I (12540) ENOCEAN_GATEWAY:   - Originator ID: 0x05804a1b
I (12550) ENOCEAN_GATEWAY:   - Data Byte: 0x30
I (12550) ENOCEAN_GATEWAY:   - Status Byte: 0x00
I (12560) ENOCEAN_GATEWAY: Button pressed! Toggling LED.
I (12890) ENOCEAN_GATEWAY: RPS Telegram Received!
I (12890) ENOCEAN_GATEWAY:   - Originator ID: 0x05804a1b
I (12900) ENOCEAN_GATEWAY:   - Data Byte: 0x20
I (12900) ENOCEAN_GATEWAY:   - Status Byte: 0x00
I (12910) ENOCEAN_GATEWAY: Button released.

Variant Notes

  • ESP32, ESP32-S2, ESP32-S3: All are excellent choices for an EnOcean gateway. They have multiple UART peripherals, providing flexibility in pin assignments.
  • ESP32-C3, ESP32-C6, ESP32-H2: These variants are also fully capable. Their primary constraint is a more limited number of available GPIOs. You must carefully select UART pins that do not conflict with essential functions like the default USB-Serial/JTAG console.
  • ESP32-C6, ESP32-H2 (802.15.4 Radio): These chips feature a radio that operates in sub-GHz bands. However, this radio is designed for IEEE 802.15.4-based protocols like Zigbee and Thread. It is not directly compatible with the EnOcean physical layer or MAC. While a “soft-modem” approach is theoretically possible, it is highly complex and not supported by any standard library. The standard, recommended method for using EnOcean with any ESP32 variant is via an external TCM connected over UART.

Common Mistakes & Troubleshooting Tips

Mistake / Issue Symptom(s) Troubleshooting / Solution
Wrong Baud Rate No data received, or monitor shows garbled characters (e.g., �����). 1. Check TCM datasheet for the correct baud rate (commonly 57600 or 9600).
2. Ensure the .baud_rate in your uart_config_t matches the TCM’s setting.
Swapped TX/RX Lines No data received by the ESP32. The TCM’s activity LED might blink, but nothing appears in the logs. Communication requires a crossover. Ensure connections are:
ESP32 TX -> TCM RX
ESP32 RX -> TCM TX
Incorrect or Unstable Power Device resets randomly. Garbled UART data. TCM module does not respond at all. 1. Ensure the TCM module is powered with the correct voltage (e.g., 3.3V or 5V). Check datasheet!
2. Connect both GND pins. A shared ground is essential.
3. Use a stable power supply; power from a laptop USB port can sometimes be noisy.
Regional Frequency Mismatch Absolutely no response from the gateway when a switch is pressed. Both devices appear to work fine independently. EnOcean hardware is region-specific. Verify that both your switch and your TCM are for the same frequency band.
EU (868 MHz) ≠ US (902 MHz)
No “Teach-In” Performed The gateway sees telegrams from other devices but not the intended one, or the device requires pairing. Some devices require a pairing (“teach-in”) process. Consult the device manual. This usually involves putting the gateway in a “learn mode” and pressing a specific button on the device.

Exercises

  1. Full EEP Decoding: Modify the gateway code to fully decode the F6-02-01 EEP. The data byte contains information about which of the four possible buttons on a dual-rocker switch was pressed. Make the gateway control four different LEDs (or print four different messages) corresponding to each button action (e.g., “Top-Left Pressed,” “Bottom-Right Pressed”).
  2. Teach-In and Whitelisting: Enhance the gateway with a “learn mode.” When a button on the ESP32 is pressed, the gateway enters learn mode for 10 seconds. The next EnOcean device ID it hears is saved to NVS (Non-Volatile Storage). Modify the main loop so that the gateway only responds to telegrams from this whitelisted ID.
  3. IoT Gateway: Combine this chapter’s code with knowledge from Volume 5. When a valid EnOcean telegram is received, instead of toggling a local LED, publish an MQTT message to a topic like enocean/switch/{originator_id}/state with a JSON payload like {"button": "A0", "state": "pressed"}. This turns your ESP32 into a true EnOcean-to-IP bridge.

Summary

  • EnOcean enables batteryless, wireless devices by harvesting ambient energy from kinetic, solar, or thermal sources.
  • It uses an efficient sub-GHz radio protocol with short telegram bursts to conserve power.
  • EnOcean Equipment Profiles (EEPs) are essential for ensuring interoperability by defining how to interpret telegram data.
  • The ESP32 communicates with the EnOcean world via an external Transceiver Module (TCM) connected over UART.
  • The ESP32’s role is to parse the EnOcean Serial Protocol (ESP3) frames sent by the TCM.
  • All ESP32 variants can function as EnOcean gateways, with pin availability being the main differentiator.
  • Building a robust gateway requires careful attention to physical wiring, UART configuration, and correct telegram parsing logic.

Further Reading

Leave a Comment

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

Scroll to Top