Ein OTBR (OpenThread Border Router) mit einem Raspberry und einem ESP32 C6
Ein C6 kostet 5€ ein alter Raspi 2 lag noch in der Schublade.

Raspi

Projektbeschreibung:
Der Border Router benötigt LAN und 5V über den USB C Stecker des ESP
Der ESP wird über USB konfiguriert, der Raspi über LAN.
Das Gerät kann dann im Betrieb irgendwo im Haus am LAN hängen. 

Meine Quellen:
https://docs.espressif.com/projects/esp-thread-br/en/latest/
https://openthread.io/platforms/co-processor?hl=de
https://github.com/espressif/esp-idf/blob/master/examples/openthread/ot_rcp/README.md
https://openthread.io/guides/border-router/espressif-esp32?hl=de

ACHTUNG:
Ein wesentlicher Stolperstein bei der Einrichtung – der mich letztlich zwei Tage Fehlersuche gekostet hat – war die korrekte Einstellung der Baudrate.

In der idf.py menuconfig lässt sich lediglich die Baudrate der USB‑Schnittstelle konfigurieren. Die eigentliche Kommunikation zwischen Raspberry Pi und ESP erfolgt jedoch über eine separate Schnittstelle, nämlich über Spinel/HDLC via UART.

Die Baudrate dieser UART‑Schnittstelle kann nicht über das Menü eingestellt werden, sondern muss manuell in einer entsprechenden Konfigurationsdatei angepasst werden.

Hier meine Notizen ohne Gewähr auf Vollständigkeit:

Auf Raspi 2

Raspberry Pi OS Lite (32-bit) mit dem Raspberry Pi Imager installieren
https://www.raspberrypi.com/software/?utm_source=chatgpt.com
sudo raspi-config (enable SSH)
Mit LAN und PuTTY per SSH verbinden

OTBR installieren:
git clone https://github.com/openthread/ot-br-posix
cd ot-br-posix

sudo apt update
sudo apt install -y git build-essential cmake ninja-build \
python3 python3-pip python3-setuptools python3-wheel \
libavahi-client-dev libreadline-dev libncurses-dev
./script/bootstrap

Schnittstelle vom ESP suchen ls /dev/tty* (vermutlich /dev/ttyACM0)
INFRA_IF_NAME=eth0 ./script/setup
sudo systemctl status otbr-agent
http://<IP-Adresse>:8080
sudo nano /etc/default/otbr-agent
OTBR_AGENT_OPTS="-I wpan0 -B eth0 spinel+hdlc+uart:///dev/ttyACM0"

Auf ESP 32 C6

Download V5.5.3 von https://dl.espressif.com/dl/esp-idf/
Die ESP IDF Powershell starten
Ins Verzeichnis OT-RCP
cd "C:\Espressif\frameworks\esp-idf-v5.5.3\examples\openthread\ot_rcp"
idf.py set-target esp32c6
idf.py fullclean

In der Datei die Baudrate setzen
C:\Espressif\frameworks\esp-idf-v5.5.3\examples\openthread\ot_rcp\main\esp_ot_config.h
#define OPENTHREAD_RCP_UART_BAUDRATE 115200

idf.py menuconfig
[*] Configure RCP UART pin manually
(4) The number of RX pin
(5) The number of TX pin
Unter Component config -> OpenThread -> ativieren und
-> openThread Spinel -> enable

/übersetzen und flaschen
idf.py fullclean
idf.py build
idf.py -p COM3 erase_flash
idf.py -p COM3 flash

sudo systemctl restart otbr-agent / OTBR neu starten
journalctl -u otbr-agent -f /journal gucken
_________________________________________

screen /dev/ttyAMA0 115200
Drücke für raus:Ctrl + A dann: K

/Thread testen
sudo ot-ctl state
sudo ot-ctl ipaddr

____________________________________________________________ Und hier die komplette Datei esp_ot_config.h:
 

/* esp_ot_config.h in C:\Espressif\frameworks\esp-idf-v5.5.3\examples\openthread\ot_rcp\main
_________________________________________
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*
* OpenThread Radio Co-Processor (RCP) Example
*
* This example code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/

#pragma once

#include "esp_openthread_types.h"
#include "driver/uart.h"

/*
* RADIO MODE:
* Für das RCP-Beispiel bleibt der Funk auf dem ESP32-C6 "native" (der C6 stellt die 15.4-Radio-Funktion bereit).
* Der Host (OTBR) spricht per Spinel/HDLC über UART mit dem RCP.
*/
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = RADIO_MODE_NATIVE, \
}

/* --------------------------
* UART RCP – Konfiguration
* -------------------------- */

/* UART-Port auswählen:
* - Falls im Kconfig vorhanden, nimm den eingestellten Port.
* - Sonst Default: Port 1 (praktisch, wenn UART0 als USB/JTAG-Konsole genutzt wird).
*/
#ifdef CONFIG_OPENTHREAD_UART_PORT
#define OPENTHREAD_RCP_UART_PORT CONFIG_OPENTHREAD_UART_PORT
#else
#define OPENTHREAD_RCP_UART_PORT 1
#endif

/* Pins:
* Wenn im Kconfig "manual pins" aktiv ist, verwende die dort konfigurierten GPIOs.
* Andernfalls (oder wenn du Pins fest verdrahtest), kannst du hier NO_CHANGE lassen
* und die Pins über das UART-Treiber-API später setzen.
*/
#if CONFIG_OPENTHREAD_UART_PIN_MANUAL
#define OPENTHREAD_RCP_UART_RX_PIN CONFIG_OPENTHREAD_UART_RX_PIN
#define OPENTHREAD_RCP_UART_TX_PIN CONFIG_OPENTHREAD_UART_TX_PIN
#else
#define OPENTHREAD_RCP_UART_RX_PIN UART_PIN_NO_CHANGE
#define OPENTHREAD_RCP_UART_TX_PIN UART_PIN_NO_CHANGE
#endif

/* Baudrate für Spinel/HDLC über UART.
* Diese MUSS zur OTBR-URL passen, z. B.:
* spinel+hdlc+uart:///dev/ttyAMA0?uart-baudrate=115200
*
* Hinweis: Espressif-Docs erwähnen teils 460800 für andere Use-Cases,
* für OTBR ist 115200 ohne Flow-Control die stabilste Standardwahl.
*/
#ifndef OPENTHREAD_RCP_UART_BAUDRATE
#define OPENTHREAD_RCP_UART_BAUDRATE 115200
#endif

/* Hardware-Flow-Control:
* Für Spinel/HDLC + 115200 Baud NICHT erforderlich.
* Falls du CTS/RTS physisch verdrahtest und im Host aktivierst, kannst du hier auf ENABLE wechseln.
*/
#ifndef OPENTHREAD_RCP_UART_HW_FLOWCTRL
#define OPENTHREAD_RCP_UART_HW_FLOWCTRL UART_HW_FLOWCTRL_DISABLE
#endif

#if CONFIG_OPENTHREAD_RCP_UART
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = HOST_CONNECTION_MODE_RCP_UART, \
.host_uart_config = { \
.port = OPENTHREAD_RCP_UART_PORT, \
.uart_config = { \
.baud_rate = OPENTHREAD_RCP_UART_BAUDRATE, \
.data_bits = UART_DATA_8_BITS, \
.parity = UART_PARITY_DISABLE, \
.stop_bits = UART_STOP_BITS_1, \
.flow_ctrl = OPENTHREAD_RCP_UART_HW_FLOWCTRL, \
.rx_flow_ctrl_thresh = 0, \
.source_clk = UART_SCLK_DEFAULT, \
}, \
.rx_pin = OPENTHREAD_RCP_UART_RX_PIN, \
.tx_pin = OPENTHREAD_RCP_UART_TX_PIN, \
}, \
}

#elif CONFIG_OPENTHREAD_RCP_SPI /* --------- SPI RCP (optional, unverändert) ---------- */

#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = HOST_CONNECTION_MODE_RCP_SPI, \
.spi_slave_config = { \
.host_device = SPI2_HOST, \
.bus_config = { \
.mosi_io_num = 3, \
.miso_io_num = 1, \
.sclk_io_num = 0, \
.quadhd_io_num = -1, \
.quadwp_io_num = -1, \
.isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO, \
}, \
.slave_config = { \
.mode = 0, \
.spics_io_num = 2, \
.queue_size = 3, \
.flags = 0, \
}, \
.intr_pin = 9, \
}, \
}

#else /* ---------------- USB SERIAL/JTAG Fallback (unverändert) ---------------------- */

#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = HOST_CONNECTION_MODE_RCP_USB, \
.host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(), \
}

#endif /* CONFIG_OPENTHREAD_RCP_UART / SPI / USB */

/* Port/Task Defaults (unverändert) */
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \
{ \
.storage_partition_name = "nvs", \
.netif_queue_size = 10, \
.task_queue_size = 10, \
}