Yes, you heard me right. In this post I’m going to describe how to make your own cheap Thread Border Router for just ~$5.
Steps
1. Get the materials
- An ESP32-C6 board with a USB port.
- Any ESP32-C6 board will work, but the Seeed Studio XIAO ESP32-C6 is highly recommended for the best range and stability.
-
(Optional) An external U.FL antenna for better coverage.
- This is highly recommended. Line of sight is not required for communication: signals can bounce and spread around, even unshielded boards without an antenna can communicate across nearby rooms. But using an external antenna lets you reliably punch through multiple walls or dense materials like brick or concrete.
- Antennas are not plug and play! In most cases, you’ll need to switch your board from using the internal antenna to use the external antenna yourself. If you buy the XIAO ESP32-C6 board mentioned above, this can be done entirely in the software, and the instructions are available below.
- 4.5dBi - 5dBi gain antennas are fine, higher gain values don’t always mean better range.
-
A USB-C cable that can transfer data.
- Beware, cheap phone charger cables sometimes don’t have data pins.
2. Make the ESP board an OpenThread RCP (Radio Co-Processor)
You’ll need a Linux machine or VM for these steps.
You can create a Linux container with all dependencies included with distrobox:
distrobox create --image debian:latest --name esp-idf --additional-packages "git python3.13 libusb-1.0-0 python3-venv cmake micro"
You can then enter the container with:
distrobox enter esp-idf
If you don’t want to use distrobox, you’ll need to manually install the following packages (listed are apt names):
python3.13(any version above 9 is fine)libusb-1.0-0python3-venvcmake
1. Install esp-idf toolchain
Run the commands below. A fast internet connection is recommended.
rm -rf cheap-esp-tbr && mkdir cheap-esp-tbr && cd cheap-esp-tbr
git clone -b v6.0-beta1 --recursive https://github.com/espressif/esp-idf.git .
./install.sh esp32-c6
2. Configure the firmware
Now we’ll configure the firmware in order for the board to communicate with OTBR over USB.
. ./export.sh
cd examples/openthread/ot_rcp
export LC_ALL=C.UTF-8 TERM=xterm
idf.py set-target esp32c6
# Open the configuration menu
idf.py menuconfig
In the menu, navigate to: Component config → OpenThread → Thread Core Features → Thread Radio Co-Processor Feature → The RCP transport type and select USB, then save and quit by pressing s, then esc, and then q.
3. (Optional) Connect the external antenna
If you got an external U.FL antenna, now is the time to connect it to your board. Align it straight and press straight down until you feel/hear a soft click. Be careful, U.FL connectors are delicate.
Now, you’ll need to switch the board to use the external antenna. How to do this is entirely board-dependent, most boards have a jumper to solder to switch. Here, we provide the specific instructions for the Seeed Studio XIAO ESP32-C6 board.
Click to show instructions for XIAO ESP32-C6
The documentation states that we need to set the pin 3 to LOW and the pin 14 HIGH to select the external antenna. We can modify the OpenThread RCP example to achieve this.
In the distrobox terminal, open the program file with the micro editor:
micro main/esp_ot_rcp.c
First delete the existing file contents and then copy and paste the modified program below:
/*
* 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-1.0 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.
*/
#include <stdio.h>
#include <unistd.h>
#include "esp_event.h"
#include "nvs_flash.h"
#include "esp_openthread.h"
#include "esp_ot_config.h"
#include "esp_vfs_eventfd.h"
#if CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE
#include "ot_examples_common.h"
#endif
#if !SOC_IEEE802154_SUPPORTED
#error "RCP is only supported for the SoCs which have IEEE 802.15.4 module"
#endif
#define TAG "ot_esp_rcp"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static void xiao_select_external_antenna(void)
{
gpio_set_direction(GPIO_NUM_3, GPIO_MODE_OUTPUT);
gpio_set_level(GPIO_NUM_3, 0);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_direction(GPIO_NUM_14, GPIO_MODE_OUTPUT);
gpio_set_level(GPIO_NUM_14, 1);
}
extern void otAppNcpInit(otInstance *instance);
void app_main(void)
{
xiao_select_external_antenna();
// Used eventfds:
// * ot task queue
// * radio driver
esp_vfs_eventfd_config_t eventfd_config = {
.max_fds = 2,
};
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
#if CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE
ot_external_coexist_init();
#endif
static esp_openthread_platform_config_t config = {
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
};
ESP_ERROR_CHECK(esp_openthread_start(&config));
}
You can save and quit by pressing Ctrl+S and then Ctrl+Q.
3. Flash the firmware
Connect your ESP32-C6 board to your computer with a USB cable and run:
idf.py build flash
If idf.py can’t auto-detect which USB port your board is connected to, you can supply the port with -p argument.
3. Set up Home Assistant
-
Connect the ESP32-C6 board to one of your HA server’s USB ports.
-
Install the Open Thread Border Router add-on.
-
In the add-on’s configuration:
- Under Device, select the option:
/dev/serial/by-id/usb-Espressif_USB_JTAG....
Don’t select the option/dev/ttyAMA..., that won’t work. - Disable Hardware Flow Control
- Under Device, select the option:
-
Start the addon.
4. Share network credentials with your phone
If you want to add Matter-based Thread devices, your phone needs to know the credentials of your Thread network. Follow the instructions in step 3 here to share the credentials with your phone using the Home Assistant Companion app.
Now you have a Thread Border Router for (possibly) as low as ~$5!
I’m just a student tinkering with Home Assistant in my spare time. If this guide helped you, consider making a donation here.
Feel free to leave any questions, suggestions, or your experiences setting this up in the comments below.
Notes
- You can use an ESP32-H2 board too. To do so, replace the board name in the above commands. ESP32-C6 is preferred because it’s both cheaper (at the moment of writing) and more performant.
- Standard advice on avoiding RF interference applies here since this is a USB-connected radio dongle operating at 2.4GHz.
- If you’ve purchased an ESP board without an external antenna connector, you can still modify it to fit one.


