mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/otbr-example' into 'master'
openthread: add platform UDP and border router example See merge request espressif/esp-idf!13885
This commit is contained in:
commit
30524406f0
@ -58,6 +58,13 @@ menu "OpenThread"
|
||||
Select this option to enable Joiner in OpenThread. This allows a device to join the Thread network with a
|
||||
pre-shared key using the Thread commissioning protocol.
|
||||
|
||||
config OPENTHREAD_BORDER_ROUTER
|
||||
bool "Enable Border Router"
|
||||
depends on OPENTHREAD_ENABLED
|
||||
default n
|
||||
help
|
||||
Select this option to enable border router features in OpenThread.
|
||||
|
||||
config OPENTHREAD_PARTITION_NAME
|
||||
string "The partition for OpenThread to store its network data"
|
||||
depends on OPENTHREAD_ENABLED
|
||||
@ -72,4 +79,11 @@ menu "OpenThread"
|
||||
help
|
||||
The size of the packet queue for OpenThread lwIP network interface.
|
||||
|
||||
config OPENTHREAD_TASK_QUEUE_SIZE
|
||||
int "The size of the OpenThread task queue"
|
||||
depends on OPENTHREAD_ENABLED
|
||||
default 10
|
||||
help
|
||||
The size of the OpenThread task queue.
|
||||
|
||||
endmenu
|
||||
|
51
components/openthread/include/esp_openthread_border_router.h
Normal file
51
components/openthread/include/esp_openthread_border_router.h
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_types.h"
|
||||
#include "esp_openthread.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initializes the border router features of OpenThread.
|
||||
*
|
||||
* @note Calling this function will make the device behave as an OpenThread
|
||||
* border router. Kconfig option CONFIG_OPENTHREAD_BORDER_ROUTER is required.
|
||||
*
|
||||
* @param[in] backbone_netif The backbone network interface (WiFi or ethernet)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_SUPPORTED if feature not supported
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_openthread_border_router_init(esp_netif_t *backbone_netif);
|
||||
|
||||
/**
|
||||
* @brief Gets the backbone interface of OpenThread border router.
|
||||
*
|
||||
* @return
|
||||
* The backbone interface or NULL if border router not initialized.
|
||||
*
|
||||
*/
|
||||
esp_netif_t *esp_openthread_get_backbone_netif(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -15,6 +15,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_openthread_types.h"
|
||||
#include "openthread/instance.h"
|
||||
|
||||
@ -38,6 +39,15 @@ void *esp_openthread_netif_glue_init(void);
|
||||
*/
|
||||
void esp_openthread_netif_glue_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief This function acquires the OpenThread netif.
|
||||
*
|
||||
* @return
|
||||
* The OpenThread netif or NULL if not initialzied.
|
||||
*
|
||||
*/
|
||||
esp_netif_t *esp_openthread_get_netif(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -72,23 +72,23 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define OPENTHREAD_CONFIG_LOG_API 1
|
||||
#define OPENTHREAD_CONFIG_LOG_ARP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_BBR 1
|
||||
#define OPENTHREAD_CONFIG_LOG_CLI 1
|
||||
#define OPENTHREAD_CONFIG_LOG_COAP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_DUA 1
|
||||
#define OPENTHREAD_CONFIG_LOG_ICMP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_IP6 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MAC 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MEM 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MESHCOP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MLE 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MLR 1
|
||||
#define OPENTHREAD_CONFIG_LOG_NETDATA 1
|
||||
#define OPENTHREAD_CONFIG_LOG_NETDIAG 1
|
||||
#define OPENTHREAD_CONFIG_LOG_PKT_DUMP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_PLATFORM 1
|
||||
#define OPENTHREAD_CONFIG_LOG_API 1
|
||||
#define OPENTHREAD_CONFIG_LOG_ARP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_BBR 1
|
||||
#define OPENTHREAD_CONFIG_LOG_CLI 1
|
||||
#define OPENTHREAD_CONFIG_LOG_COAP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_DUA 1
|
||||
#define OPENTHREAD_CONFIG_LOG_ICMP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_IP6 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MAC 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MEM 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MESHCOP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MLE 1
|
||||
#define OPENTHREAD_CONFIG_LOG_MLR 1
|
||||
#define OPENTHREAD_CONFIG_LOG_NETDATA 1
|
||||
#define OPENTHREAD_CONFIG_LOG_NETDIAG 1
|
||||
#define OPENTHREAD_CONFIG_LOG_PKT_DUMP 1
|
||||
#define OPENTHREAD_CONFIG_LOG_PLATFORM 1
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS
|
||||
@ -115,6 +115,40 @@
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
|
||||
*
|
||||
* Define to 1 to enable platform UDP support.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
|
||||
*
|
||||
* Define to 1 to enable platform NETIF support.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
|
||||
#define OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_OPENTHREAD_BORDER_ROUTER
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
|
||||
*
|
||||
* Define to 1 to enable Border Agent support.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_OPENTHREAD_BORDER_ROUTER
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
*
|
||||
|
34
components/openthread/port/esp_openthread_border_router.c
Normal file
34
components/openthread/port/esp_openthread_border_router.c
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
|
||||
#include "esp_openthread_border_router.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
static esp_netif_t *s_backbone_netif = NULL;
|
||||
|
||||
esp_err_t esp_openthread_border_router_init(esp_netif_t *backbone_if)
|
||||
{
|
||||
#if CONFIG_OPENTHREAD_BORDER_ROUTER
|
||||
s_backbone_netif = backbone_if;
|
||||
|
||||
return ESP_OK;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_netif_t *esp_openthread_get_backbone_netif(void)
|
||||
{
|
||||
return s_backbone_netif;
|
||||
}
|
@ -51,6 +51,7 @@ static esp_openthread_netif_glue_t s_openthread_netif_glue = {
|
||||
ESP_EVENT_DEFINE_BASE(OPENTHREAD_EVENT);
|
||||
|
||||
static QueueHandle_t s_packet_queue;
|
||||
static esp_netif_t *s_openthread_netif;
|
||||
|
||||
#define NETIF_OUTPUT_SIGNAL 1
|
||||
|
||||
@ -265,6 +266,7 @@ static esp_err_t openthread_netif_post_attach(esp_netif_t *esp_netif, void *args
|
||||
|
||||
otLogInfoPlat("OpenThread attached to netif");
|
||||
esp_err_t error = register_openthread_event_handlers(esp_netif);
|
||||
s_openthread_netif = esp_netif;
|
||||
if (error == ESP_OK) {
|
||||
error = esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_START, NULL, 0, 0);
|
||||
}
|
||||
@ -323,6 +325,7 @@ void esp_openthread_netif_glue_deinit(void)
|
||||
if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_STOP, NULL, 0, 0) != ESP_OK) {
|
||||
otLogCritPlat("Failed to stop OpenThread netif");
|
||||
}
|
||||
s_openthread_netif = NULL;
|
||||
unregister_openthread_event_handlers();
|
||||
}
|
||||
|
||||
@ -343,3 +346,8 @@ esp_err_t esp_openthread_netif_glue_process(otInstance *instance, const esp_open
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_netif_t *esp_openthread_get_netif(void)
|
||||
{
|
||||
return s_openthread_netif;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "esp_openthread_netif_glue.h"
|
||||
#include "esp_openthread_netif_glue_priv.h"
|
||||
#include "esp_openthread_radio_uart.h"
|
||||
#include "esp_openthread_task_queue.h"
|
||||
#include "esp_openthread_types.h"
|
||||
#include "esp_openthread_uart.h"
|
||||
#include "common/code_utils.hpp"
|
||||
@ -53,6 +54,7 @@ esp_err_t esp_openthread_platform_init(const esp_openthread_platform_config_t *c
|
||||
if (config->host_config.host_connection_mode == HOST_CONNECTION_MODE_UART) {
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_uart_init(config), exit, OT_PLAT_LOG_TAG, "esp_openthread_uart_init failed");
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_task_queue_init(), exit, OT_PLAT_LOG_TAG, "esp_openthread_task_queue_init failed");
|
||||
ESP_GOTO_ON_ERROR(esp_openthread_radio_init(config), exit, OT_PLAT_LOG_TAG, "esp_openthread_radio_init failed");
|
||||
|
||||
exit:
|
||||
@ -73,6 +75,7 @@ esp_err_t esp_openthread_platform_deinit(void)
|
||||
ESP_RETURN_ON_FALSE(s_openthread_platform_initialized, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG,
|
||||
"OpenThread platform not initialized");
|
||||
|
||||
esp_openthread_task_queue_deinit();
|
||||
esp_openthread_radio_deinit();
|
||||
if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_UART) {
|
||||
esp_openthread_uart_deinit();
|
||||
@ -90,6 +93,7 @@ void esp_openthread_platform_update(esp_openthread_mainloop_context_t *mainloop)
|
||||
}
|
||||
esp_openthread_radio_update(mainloop);
|
||||
esp_openthread_netif_glue_update(mainloop);
|
||||
esp_openthread_task_queue_update(mainloop);
|
||||
}
|
||||
|
||||
esp_err_t esp_openthread_platform_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop)
|
||||
@ -99,5 +103,6 @@ esp_err_t esp_openthread_platform_process(otInstance *instance, const esp_openth
|
||||
}
|
||||
esp_openthread_radio_process(instance, mainloop);
|
||||
esp_openthread_alarm_process(instance);
|
||||
esp_openthread_task_queue_process(instance, mainloop);
|
||||
return esp_openthread_netif_glue_process(instance, mainloop);
|
||||
}
|
||||
|
102
components/openthread/port/esp_openthread_task_queue.c
Normal file
102
components/openthread/port/esp_openthread_task_queue.c
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_openthread_common_macro.h"
|
||||
#include "esp_openthread_task_queue.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_eventfd.h"
|
||||
#include "common/logging.hpp"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
static QueueHandle_t s_task_queue = NULL;
|
||||
static int s_task_queue_event_fd = -1;
|
||||
|
||||
typedef struct {
|
||||
esp_openthread_task_t task;
|
||||
void *arg;
|
||||
} task_storage_t;
|
||||
|
||||
esp_err_t esp_openthread_task_queue_init(void)
|
||||
{
|
||||
s_task_queue_event_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
ESP_RETURN_ON_FALSE(s_task_queue_event_fd >= 0, ESP_FAIL, OT_PLAT_LOG_TAG,
|
||||
"Failed to create OpenThread task queue event fd");
|
||||
s_task_queue = xQueueCreate(CONFIG_OPENTHREAD_TASK_QUEUE_SIZE, sizeof(task_storage_t));
|
||||
ESP_RETURN_ON_FALSE(s_task_queue != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG,
|
||||
"Failed to create OpenThread task queue");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_openthread_task_queue_post(esp_openthread_task_t task, void *arg)
|
||||
{
|
||||
task_storage_t task_storage = {
|
||||
.task = task,
|
||||
.arg = arg,
|
||||
};
|
||||
uint64_t val = 1;
|
||||
ssize_t ret;
|
||||
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(s_task_queue, &task_storage, portMAX_DELAY), ESP_FAIL, OT_PLAT_LOG_TAG,
|
||||
"Failed to post task to OpenThread task queue");
|
||||
ret = write(s_task_queue_event_fd, &val, sizeof(val));
|
||||
assert(ret == sizeof(val));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_openthread_task_queue_update(esp_openthread_mainloop_context_t *mainloop)
|
||||
{
|
||||
if (s_task_queue_event_fd >= 0) {
|
||||
FD_SET(s_task_queue_event_fd, &mainloop->read_fds);
|
||||
if (s_task_queue_event_fd > mainloop->max_fd) {
|
||||
mainloop->max_fd = s_task_queue_event_fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_openthread_task_queue_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop)
|
||||
{
|
||||
task_storage_t task_storage;
|
||||
|
||||
if (FD_ISSET(s_task_queue_event_fd, &mainloop->read_fds)) {
|
||||
uint64_t val;
|
||||
ssize_t ret = read(s_task_queue_event_fd, &val, sizeof(val));
|
||||
assert(ret == sizeof(val));
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_FALSE(s_task_queue != NULL, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG,
|
||||
"OpenThread task queue not initialized");
|
||||
while (xQueueReceive(s_task_queue, &task_storage, 0) == pdTRUE) {
|
||||
task_storage.task(task_storage.arg);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_openthread_task_queue_deinit(void)
|
||||
{
|
||||
if (s_task_queue) {
|
||||
vQueueDelete(s_task_queue);
|
||||
s_task_queue = NULL;
|
||||
}
|
||||
if (s_task_queue_event_fd >= 0) {
|
||||
close(s_task_queue_event_fd);
|
||||
s_task_queue_event_fd = -1;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
360
components/openthread/port/esp_openthread_udp.c
Normal file
360
components/openthread/port/esp_openthread_udp.c
Normal file
@ -0,0 +1,360 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_openthread.h"
|
||||
#include "esp_openthread_border_router.h"
|
||||
#include "esp_openthread_common_macro.h"
|
||||
#include "esp_openthread_lock.h"
|
||||
#include "esp_openthread_netif_glue.h"
|
||||
#include "esp_openthread_task_queue.h"
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/logging.hpp"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/prot/ip4.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "openthread/error.h"
|
||||
#include "openthread/platform/udp.h"
|
||||
|
||||
typedef struct {
|
||||
otUdpSocket *socket;
|
||||
struct pbuf *recv_buf;
|
||||
ip_addr_t addr;
|
||||
uint16_t port;
|
||||
uint8_t hop_limit;
|
||||
bool is_host_interface;
|
||||
} udp_recv_task_t;
|
||||
|
||||
typedef struct {
|
||||
TaskHandle_t source_task;
|
||||
otUdpSocket *socket;
|
||||
struct udp_pcb *pcb_ret;
|
||||
} udp_new_task_t;
|
||||
|
||||
typedef struct {
|
||||
TaskHandle_t source_task;
|
||||
struct udp_pcb *pcb;
|
||||
ip_addr_t addr;
|
||||
uint16_t port;
|
||||
err_t ret;
|
||||
} udp_bind_connect_task_t;
|
||||
|
||||
typedef struct {
|
||||
TaskHandle_t source_task;
|
||||
struct udp_pcb *pcb;
|
||||
uint8_t netif_index;
|
||||
} udp_bind_netif_task_t;
|
||||
|
||||
typedef struct {
|
||||
struct udp_pcb *pcb;
|
||||
otMessage *message;
|
||||
ip_addr_t addr;
|
||||
uint16_t port;
|
||||
bool multicast_loop;
|
||||
uint8_t hop_limit;
|
||||
uint8_t netif_index;
|
||||
} udp_send_task_t;
|
||||
|
||||
static void wait_for_task_notification(void)
|
||||
{
|
||||
esp_openthread_lock_release();
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
}
|
||||
|
||||
static ip_addr_t map_openthread_addr_to_lwip_addr(const otIp6Address *address)
|
||||
{
|
||||
ip_addr_t addr;
|
||||
|
||||
memcpy(ip_2_ip6(&addr)->addr, address->mFields.m8, sizeof(ip_2_ip6(&addr)->addr));
|
||||
if (ip6_addr_isipv4mappedipv6(ip_2_ip6(&addr))) {
|
||||
unmap_ipv4_mapped_ipv6(ip_2_ip4(&addr), ip_2_ip6(&addr));
|
||||
addr.type = IPADDR_TYPE_V4;
|
||||
} else {
|
||||
addr.type = IPADDR_TYPE_V6;
|
||||
#if LWIP_IPV6_SCOPES
|
||||
addr.u_addr.ip6.zone = IP6_NO_ZONE;
|
||||
#endif
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void udp_recv_task(void *ctx)
|
||||
{
|
||||
udp_recv_task_t *task = (udp_recv_task_t *)ctx;
|
||||
|
||||
otMessageInfo message_info;
|
||||
otMessage *message = NULL;
|
||||
otMessageSettings msg_settings = {.mLinkSecurityEnabled = false, .mPriority = OT_MESSAGE_PRIORITY_NORMAL};
|
||||
struct pbuf *recv_buf = task->recv_buf;
|
||||
uint8_t *data_buf = (uint8_t *)recv_buf->payload;
|
||||
uint8_t *data_buf_to_free = NULL;
|
||||
|
||||
message_info.mSockPort = 0;
|
||||
memset(&message_info.mSockAddr, 0, sizeof(message_info.mSockAddr));
|
||||
message_info.mHopLimit = task->hop_limit;
|
||||
message_info.mPeerPort = task->port;
|
||||
if (task->addr.type == IPADDR_TYPE_V4) {
|
||||
ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&task->addr), ip_2_ip4(&task->addr));
|
||||
}
|
||||
memcpy(&message_info.mPeerAddr, ip_2_ip6(&task->addr)->addr, sizeof(message_info.mPeerAddr));
|
||||
|
||||
if (recv_buf->next != NULL) {
|
||||
data_buf = (uint8_t *)malloc(recv_buf->tot_len);
|
||||
if (data_buf != NULL) {
|
||||
data_buf_to_free = data_buf;
|
||||
pbuf_copy_partial(recv_buf, data_buf, recv_buf->tot_len, 0);
|
||||
}
|
||||
}
|
||||
VerifyOrExit(data_buf != NULL,
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate data buf when receiving OpenThread plat UDP"));
|
||||
message = otUdpNewMessage(esp_openthread_get_instance(), &msg_settings);
|
||||
VerifyOrExit(message != NULL,
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate OpenThread message when receiving OpenThread plat UDP"));
|
||||
VerifyOrExit(otMessageAppend(message, data_buf, recv_buf->tot_len) == OT_ERROR_NONE,
|
||||
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to copy OpenThread message when receiving OpenThread plat UDP"));
|
||||
task->socket->mHandler(task->socket->mContext, message, &message_info);
|
||||
otMessageFree(message);
|
||||
|
||||
exit:
|
||||
free(task);
|
||||
if (data_buf_to_free) {
|
||||
free(data_buf_to_free);
|
||||
}
|
||||
pbuf_free(recv_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
static void handle_udp_recv(void *ctx, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, uint16_t port)
|
||||
{
|
||||
udp_recv_task_t *task = (udp_recv_task_t *)malloc(sizeof(udp_recv_task_t));
|
||||
const struct ip6_hdr *ip6_hdr = ip6_current_header();
|
||||
const struct ip_hdr *ip4_hdr = ip4_current_header();
|
||||
struct netif *source_netif = ip_current_netif();
|
||||
|
||||
if (task == NULL) {
|
||||
otLogCritPlat("Failed to allocate recv task when receiving OpenThread plat UDP");
|
||||
}
|
||||
task->socket = (otUdpSocket *)ctx;
|
||||
task->recv_buf = p;
|
||||
task->addr = *addr;
|
||||
task->port = port;
|
||||
task->hop_limit = (addr->type == IPADDR_TYPE_V6) ? IP6H_HOPLIM(ip6_hdr) : IPH_TTL(ip4_hdr);
|
||||
task->is_host_interface =
|
||||
(netif_get_index(source_netif) == esp_netif_get_netif_impl_index(esp_openthread_get_backbone_netif()));
|
||||
|
||||
if (esp_openthread_task_queue_post(udp_recv_task, task) != ESP_OK) {
|
||||
free(task);
|
||||
}
|
||||
}
|
||||
|
||||
static void udp_new_task(void *ctx)
|
||||
{
|
||||
udp_new_task_t *task = (udp_new_task_t *)ctx;
|
||||
|
||||
task->pcb_ret = udp_new();
|
||||
udp_recv(task->pcb_ret, handle_udp_recv, task->socket);
|
||||
xTaskNotifyGive(task->source_task);
|
||||
}
|
||||
|
||||
otError otPlatUdpSocket(otUdpSocket *udp_socket)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
udp_new_task_t task = {.source_task = xTaskGetCurrentTaskHandle(), .socket = udp_socket};
|
||||
tcpip_callback(udp_new_task, &task);
|
||||
wait_for_task_notification();
|
||||
VerifyOrExit(task.pcb_ret != NULL, error = OT_ERROR_FAILED);
|
||||
udp_socket->mHandle = task.pcb_ret;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
static void udp_close_task(void *ctx)
|
||||
{
|
||||
struct udp_pcb *pcb = (struct udp_pcb *)ctx;
|
||||
|
||||
udp_remove(pcb);
|
||||
}
|
||||
|
||||
otError otPlatUdpClose(otUdpSocket *udp_socket)
|
||||
{
|
||||
struct udp_pcb *pcb = (struct udp_pcb *)udp_socket->mHandle;
|
||||
|
||||
if (pcb) {
|
||||
tcpip_callback(udp_close_task, pcb);
|
||||
}
|
||||
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
static void udp_bind_task(void *ctx)
|
||||
{
|
||||
udp_bind_connect_task_t *task = (udp_bind_connect_task_t *)ctx;
|
||||
|
||||
task->ret = udp_bind(task->pcb, &task->addr, task->port);
|
||||
xTaskNotifyGive(task->source_task);
|
||||
}
|
||||
|
||||
otError otPlatUdpBind(otUdpSocket *udp_socket)
|
||||
{
|
||||
udp_bind_connect_task_t task = {
|
||||
.source_task = xTaskGetCurrentTaskHandle(),
|
||||
.pcb = (struct udp_pcb *)udp_socket->mHandle,
|
||||
.port = udp_socket->mSockName.mPort,
|
||||
};
|
||||
ESP_LOGI(OT_PLAT_LOG_TAG, "Platform UDP bound to port %d", udp_socket->mSockName.mPort);
|
||||
|
||||
task.addr.type = IPADDR_TYPE_ANY;
|
||||
memcpy(ip_2_ip6(&task.addr)->addr, udp_socket->mSockName.mAddress.mFields.m8, sizeof(ip_2_ip6(&task.addr)->addr));
|
||||
tcpip_callback(udp_bind_task, &task);
|
||||
wait_for_task_notification();
|
||||
|
||||
return task.ret == ERR_OK ? OT_ERROR_NONE : OT_ERROR_FAILED;
|
||||
}
|
||||
|
||||
static void udp_bind_netif_task(void *ctx)
|
||||
{
|
||||
udp_bind_netif_task_t *task = (udp_bind_netif_task_t *)ctx;
|
||||
|
||||
task->netif_index = task->netif_index;
|
||||
xTaskNotifyGive(task->source_task);
|
||||
}
|
||||
|
||||
static uint8_t get_netif_index(otNetifIdentifier netif_identifier)
|
||||
{
|
||||
switch (netif_identifier) {
|
||||
case OT_NETIF_UNSPECIFIED:
|
||||
return NETIF_NO_INDEX;
|
||||
case OT_NETIF_THREAD:
|
||||
return esp_netif_get_netif_impl_index(esp_openthread_get_netif());
|
||||
case OT_NETIF_BACKBONE:
|
||||
return esp_netif_get_netif_impl_index(esp_openthread_get_backbone_netif());
|
||||
default:
|
||||
return NETIF_NO_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
otError otPlatUdpBindToNetif(otUdpSocket *udp_socket, otNetifIdentifier netif_identifier)
|
||||
{
|
||||
udp_bind_netif_task_t task = {
|
||||
.source_task = xTaskGetCurrentTaskHandle(),
|
||||
.pcb = (struct udp_pcb *)udp_socket->mHandle,
|
||||
.netif_index = get_netif_index(netif_identifier),
|
||||
};
|
||||
|
||||
tcpip_callback(udp_bind_netif_task, &task);
|
||||
wait_for_task_notification();
|
||||
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
static void udp_connect_task(void *ctx)
|
||||
{
|
||||
udp_bind_connect_task_t *task = (udp_bind_connect_task_t *)ctx;
|
||||
|
||||
task->ret = udp_connect(task->pcb, &task->addr, task->port);
|
||||
xTaskNotifyGive(task->source_task);
|
||||
}
|
||||
|
||||
otError otPlatUdpConnect(otUdpSocket *udp_socket)
|
||||
{
|
||||
udp_bind_connect_task_t task = {
|
||||
.source_task = xTaskGetCurrentTaskHandle(),
|
||||
.pcb = (struct udp_pcb *)udp_socket->mHandle,
|
||||
.port = udp_socket->mPeerName.mPort,
|
||||
};
|
||||
|
||||
task.addr = map_openthread_addr_to_lwip_addr(&udp_socket->mPeerName.mAddress);
|
||||
tcpip_callback(udp_connect_task, &task);
|
||||
wait_for_task_notification();
|
||||
|
||||
return task.ret == ERR_OK ? OT_ERROR_NONE : OT_ERROR_FAILED;
|
||||
}
|
||||
|
||||
static bool is_link_local(const otIp6Address *address)
|
||||
{
|
||||
return address->mFields.m8[0] == 0xfe && address->mFields.m8[1] == 0x80;
|
||||
}
|
||||
|
||||
static bool is_multicast(const otIp6Address *address)
|
||||
{
|
||||
return address->mFields.m8[0] == 0xff;
|
||||
}
|
||||
|
||||
static void udp_send_task(void *ctx)
|
||||
{
|
||||
udp_send_task_t *task = (udp_send_task_t *)ctx;
|
||||
struct pbuf *send_buf = NULL;
|
||||
uint16_t len = otMessageGetLength(task->message);
|
||||
|
||||
task->pcb->ttl = task->hop_limit;
|
||||
task->pcb->netif_idx = task->netif_index;
|
||||
#if LWIP_IPV6_SCOPES
|
||||
if (task->addr.type == IPADDR_TYPE_V6) {
|
||||
ip_2_ip6(&task->addr)->zone = task->netif_index;
|
||||
}
|
||||
#endif
|
||||
task->pcb->flags = (task->pcb->flags & (~UDP_FLAGS_MULTICAST_LOOP));
|
||||
if (task->multicast_loop) {
|
||||
task->pcb->flags |= UDP_FLAGS_MULTICAST_LOOP;
|
||||
}
|
||||
send_buf = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
|
||||
otMessageRead(task->message, 0, send_buf->payload, len);
|
||||
VerifyOrExit(send_buf != NULL);
|
||||
udp_sendto(task->pcb, send_buf, &task->addr, task->port);
|
||||
|
||||
exit:
|
||||
if (send_buf) {
|
||||
pbuf_free(send_buf);
|
||||
}
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
otMessageFree(task->message);
|
||||
esp_openthread_lock_release();
|
||||
free(task);
|
||||
}
|
||||
|
||||
otError otPlatUdpSend(otUdpSocket *udp_socket, otMessage *message, const otMessageInfo *message_info)
|
||||
{
|
||||
udp_send_task_t *task = (udp_send_task_t *)malloc(sizeof(udp_send_task_t));
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(task != NULL, error = OT_ERROR_NO_BUFS);
|
||||
task->pcb = (struct udp_pcb *)udp_socket->mHandle;
|
||||
task->message = message;
|
||||
task->port = message_info->mPeerPort;
|
||||
task->multicast_loop = message_info->mMulticastLoop;
|
||||
task->hop_limit = message_info->mHopLimit;
|
||||
task->netif_index = NETIF_NO_INDEX;
|
||||
task->addr = map_openthread_addr_to_lwip_addr(&message_info->mPeerAddr);
|
||||
|
||||
if (is_link_local(&message_info->mPeerAddr) || is_multicast(&message_info->mPeerAddr)) {
|
||||
task->netif_index = get_netif_index(message_info->mIsHostInterface ? OT_NETIF_BACKBONE : OT_NETIF_THREAD);
|
||||
}
|
||||
tcpip_callback(udp_send_task, task);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_openthread.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief OpenThread task declaration
|
||||
*
|
||||
*/
|
||||
typedef void (*esp_openthread_task_t)(void *);
|
||||
|
||||
/**
|
||||
* @brief This function allocs and initializes the OpenThread task queue.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NO_MEM on queue allocation failure
|
||||
* - ESP_FAIL on other failures
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_openthread_task_queue_init(void);
|
||||
|
||||
/**
|
||||
* @brief This function posts a task to the OpenThread task queue.
|
||||
*
|
||||
* @param[in] task The task to execute.
|
||||
* @param[in] arg The context argument to be passed to the task.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_openthread_task_queue_post(esp_openthread_task_t task, void *arg);
|
||||
|
||||
/**
|
||||
* @brief This function updates the task queue inner fd to the main loop.
|
||||
*
|
||||
* @param[inout] mainloop The main loop context.
|
||||
*
|
||||
*/
|
||||
void esp_openthread_task_queue_update(esp_openthread_mainloop_context_t *mainloop);
|
||||
|
||||
/**
|
||||
* @brief This function drives the execution of the task queue.
|
||||
*
|
||||
* @param[in] instance The OpenThread instance.
|
||||
* @param[in] mainloop The main loop context.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_openthread_task_queue_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop);
|
||||
|
||||
/**
|
||||
* @brief This function deinitializes the task queue.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_FAIL
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_openthread_task_queue_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
10
examples/openthread/ot_br/CMakeLists.txt
Normal file
10
examples/openthread/ot_br/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(otbr_esp)
|
10
examples/openthread/ot_br/Makefile
Normal file
10
examples/openthread/ot_br/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := otbr_esp
|
||||
|
||||
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
148
examples/openthread/ot_br/README.md
Normal file
148
examples/openthread/ot_br/README.md
Normal file
@ -0,0 +1,148 @@
|
||||
# OpenThread command line example
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates an [OpenThread border router](https://openthread.io/guides/border-router).
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware connection
|
||||
|
||||
To run this example, it's used to use an DevKit C board and connect PIN4 and PIN5 to the UART TX and RX port of another 15.4 capable radio co-processor ([RCP](https://openthread.io/platforms/co-processor?hl=en))
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
You need to configure the `CONFIG_EXAMPLE_WIFI_SSID` and `CONFIG_EXAMPLE_WIFI_PASSWORD` with your access point's ssid and psk.
|
||||
|
||||
### Build, Flash, and Run
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT build flash monitor
|
||||
```
|
||||
|
||||
## Example Output
|
||||
|
||||
```bash
|
||||
I (2729) esp_netif_handlers: example_connect: sta ip: 192.168.1.100, mask: 255.255.255.0, gw: 192.168.1.1
|
||||
I (2729) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.1.100
|
||||
I (3729) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:266f:28ff:fe80:2920, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (3729) example_connect: Connected to example_connect: sta
|
||||
I (3739) example_connect: - IPv4 address: 192.168.1.100
|
||||
I (3739) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2920, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
|
||||
......
|
||||
|
||||
|
||||
I(8139) OPENTHREAD:[INFO]-MLE-----: AttachState ParentReqReeds -> Idle
|
||||
I(8139) OPENTHREAD:[NOTE]-MLE-----: Allocate router id 50
|
||||
I(8139) OPENTHREAD:[NOTE]-MLE-----: RLOC16 fffe -> c800
|
||||
I(8159) OPENTHREAD:[NOTE]-MLE-----: Role Detached -> Leader
|
||||
```
|
||||
|
||||
The device will automatically connect to the configured WiFi and Thread network and act as the border router.
|
||||
|
||||
## Using the border agent feature
|
||||
|
||||
You need to ot-commissioner on the host machine and another Thread end device running OpenThread cli.
|
||||
|
||||
You can find the guide to build and run ot-commissioner [here](https://openthread.io/guides/commissioner/build).
|
||||
|
||||
Make sure to configure the same PSKc as the one in sdkconfig in ot-commisioner's config file `non-ccm-config.json`
|
||||
|
||||
### Connect the commissioner to the border router
|
||||
|
||||
Note that the target address `192.168.1.100` shall match the actual WiFi IP address of the device.
|
||||
|
||||
``` bash
|
||||
$ commissioner-cli /usr/local/etc/commissioner/non-ccm-config.json
|
||||
> start 192.168.1.100 49154
|
||||
[done]
|
||||
> active
|
||||
true
|
||||
[done]
|
||||
```
|
||||
|
||||
You can also verify the commissioner connection from the border router's log:
|
||||
|
||||
```
|
||||
I(59709) OPENTHREAD:[INFO]-MESH-CP-: DTLS started
|
||||
I(65469) OPENTHREAD:[INFO]-MESH-CP-: Commissioner connected
|
||||
I(65479) OPENTHREAD:[INFO]-MESH-CP-: Forwarded request to leader on c/lp
|
||||
I(65489) OPENTHREAD:[INFO]-MESH-CP-: received petition
|
||||
I(65489) OPENTHREAD:[INFO]-MESH-CP-: sent petition response
|
||||
I(65489) OPENTHREAD:[INFO]-MESH-CP-: commissioner accepted: session ID=3077, ALOC=fd04:b642:9ba9:fcdc:0:ff:fe00:fc35
|
||||
I(65499) OPENTHREAD:[INFO]-MESH-CP-: Sent to commissioner
|
||||
I(65509) OPENTHREAD:[INFO]-CORE----: Notifier: StateChanged (0x00000201) [Ip6+ NetData]
|
||||
I(65529) OPENTHREAD:[INFO]-BBR-----: PBBR state: None
|
||||
I(65539) OPENTHREAD:[INFO]-BBR-----: Domain Prefix: ::/0, state: None
|
||||
I(65559) OPENTHREAD:[INFO]-MESH-CP-: Forwarded request to leader on c/ag
|
||||
W(65559) OPENTHREAD:[WARN]-MESH-CP-: Failed to notify commissioner on ProxyRx (c/ur): DestinationAddressFiltered
|
||||
I(65579) OPENTHREAD:[INFO]-MESH-CP-: sent active dataset get response to fd04:b642:9ba9:fcdc:0:ff:fe00:c800
|
||||
W(65579) OPENTHREAD:[WARN]-MESH-CP-: Failed to notify commissioner on ProxyRx (c/ur): DestinationAddressFiltered
|
||||
I(65589) OPENTHREAD:[INFO]-MESH-CP-: Sent to commissioner
|
||||
I(65629) OPENTHREAD:[INFO]-MESH-CP-: Forwarded request to leader on c/ag
|
||||
W(65629) OPENTHREAD:[WARN]-MESH-CP-: Failed to notify commissioner on ProxyRx (c/ur): DestinationAddressFiltered
|
||||
I(65649) OPENTHREAD:[INFO]-MESH-CP-: sent active dataset get response to fd04:b642:9ba9:fcdc:0:ff:fe00:c800
|
||||
W(65649) OPENTHREAD:[WARN]-MESH-CP-: Failed to notify commissioner on ProxyRx (c/ur): DestinationAddressFiltered
|
||||
I(65659) OPENTHREAD:[INFO]-MESH-CP-: Sent to commissioner
|
||||
I(65689) OPENTHREAD:[INFO]-MESH-CP-: Proxy transmit sent to fd04:b642:9ba9:fcdc:0:ff:fe00:fc00
|
||||
W(65689) OPENTHREAD:[WARN]-MESH-CP-: Failed to notify commissioner on ProxyRx (c/ur): DestinationAddressFiltered
|
||||
I(65699) OPENTHREAD:[INFO]-MESH-CP-: sent pending dataset get response to fd04:b642:9ba9:fcdc:0:ff:fe00:fc35
|
||||
I(65709) OPENTHREAD:[INFO]-MESH-CP-: Sent to commissioner on c/ur
|
||||
I(65749) OPENTHREAD:[INFO]-MESH-CP-: Proxy transmit sent to fd04:b642:9ba9:fcdc:0:ff:fe00:fc00
|
||||
W(65749) OPENTHREAD:[WARN]-MESH-CP-: Failed to notify commissioner on ProxyRx (c/ur): DestinationAddressFiltered
|
||||
I(65759) OPENTHREAD:[INFO]-MESH-CP-: sent commissioning dataset set response
|
||||
I(65769) OPENTHREAD:[INFO]-MESH-CP-: Sent to commissioner on c/ur
|
||||
I(65769) OPENTHREAD:[INFO]-CORE----: Notifier: StateChanged (0x00000200) [NetData]
|
||||
I(65789) OPENTHREAD:[INFO]-BBR-----: PBBR state: None
|
||||
|
||||
```
|
||||
|
||||
### Commission the joiner
|
||||
|
||||
In the OT commissioner cli, run:
|
||||
``` bash
|
||||
> joiner enableall meshcop J01NU5
|
||||
[done]
|
||||
>
|
||||
```
|
||||
|
||||
In the joining device's cli, run:
|
||||
|
||||
```bash
|
||||
> ifconfig up
|
||||
Done
|
||||
> joiner start J01NU5
|
||||
Done
|
||||
> Join success!
|
||||
> thread start
|
||||
Done
|
||||
```
|
||||
|
||||
You can also find these log lines in the border router:
|
||||
|
||||
```
|
||||
I(531219) OPENTHREAD:[INFO]-MESH-CP-: Received relay transmit
|
||||
I(531229) OPENTHREAD:[INFO]-MESH-CP-: Received kek
|
||||
I(531279) OPENTHREAD:[INFO]-MAC-----: Sent IPv6 UDP msg, len:85, chksum:14a0, to:92335c4b320830fb, sec:no, prio:net
|
||||
I(531279) OPENTHREAD:[INFO]-MAC-----: src:[fe80:0:0:0:ac2f:720a:6fe4:c837]:1000
|
||||
I(531289) OPENTHREAD:[INFO]-MAC-----: dst:[fe80:0:0:0:9033:5c4b:3208:30fb]:1000
|
||||
I(531299) OPENTHREAD:[INFO]-MESH-CP-: Sending JOIN_ENT.ntf
|
||||
I(531299) OPENTHREAD:[INFO]-MESH-CP-: Sent joiner entrust length = 161
|
||||
|
||||
......
|
||||
|
||||
I(552699) OPENTHREAD:[INFO]-MLE-----: Receive Child ID Request (fe80:0:0:0:8434:c5ec:fe9f:c088)
|
||||
I(552729) OPENTHREAD:[INFO]-CORE----: [settings] Added ChildInfo {rloc:0xc801, extaddr:8634c5ecfe9fc088, timeout:240, mode:0x0f, version:3}
|
||||
I(552729) OPENTHREAD:[INFO]-MLE-----: Send Child ID Response (fe80:0:0:0:8434:c5ec:fe9f:c088,0xc801)
|
||||
I(552739) OPENTHREAD:[INFO]-CORE----: Notifier: StateChanged (0x00000400) [Child+]
|
||||
I(552749) OPENTHREAD:[INFO]-UTIL----: Starting Child Supervision
|
||||
```
|
||||
|
||||
The device has now joined the same Thread network based on the key set by the commissioner.
|
2
examples/openthread/ot_br/main/CMakeLists.txt
Normal file
2
examples/openthread/ot_br/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "esp_ot_br.c"
|
||||
INCLUDE_DIRS ".")
|
41
examples/openthread/ot_br/main/Kconfig.projbuild
Normal file
41
examples/openthread/ot_br/main/Kconfig.projbuild
Normal file
@ -0,0 +1,41 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config OPENTHREAD_NETWORK_NAME
|
||||
string "OpenThread network name"
|
||||
default "OpenThread"
|
||||
help
|
||||
The OpenThread network name for example to use
|
||||
|
||||
config OPENTHREAD_NETWORK_CHANNEL
|
||||
int "OpenThread network channel"
|
||||
range 11 26
|
||||
default 15
|
||||
help
|
||||
The OpenThread network channel to use
|
||||
|
||||
config OPENTHREAD_NETWORK_PANID
|
||||
hex "OpenThread network pan id"
|
||||
range 0 0x1234
|
||||
default 0x1234
|
||||
help
|
||||
The OpenThread network pan id to use
|
||||
|
||||
config OPENTHREAD_NETWORK_EXTPANID
|
||||
string "OpenThread extended pan id"
|
||||
default "dead00beef00cafe"
|
||||
help
|
||||
The OpenThread network extended pan id in hex string format
|
||||
|
||||
config OPENTHREAD_NETWORK_MASTERKEY
|
||||
string "OpenThread master key"
|
||||
default "00112233445566778899aabbccddeeff"
|
||||
help
|
||||
The OpenThread network master key in hex string format
|
||||
|
||||
config OPENTHREAD_NETWORK_PSKC
|
||||
string "OpenThread pre-shared commissioner key"
|
||||
default "104810e2315100afd6bc9215a6bfac53"
|
||||
help
|
||||
The OpenThread pre-shared commissioner key in hex string format
|
||||
|
||||
endmenu
|
8
examples/openthread/ot_br/main/component.mk
Normal file
8
examples/openthread/ot_br/main/component.mk
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
|
||||
COMPONENT_PRIV_INCLUDEDIRS := .
|
191
examples/openthread/ot_br/main/esp_ot_br.c
Normal file
191
examples/openthread/ot_br/main/esp_ot_br.c
Normal file
@ -0,0 +1,191 @@
|
||||
// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ip_addr.h"
|
||||
#include "esp_netif_net_stack.h"
|
||||
#include "esp_openthread.h"
|
||||
#include "esp_openthread_border_router.h"
|
||||
#include "esp_openthread_defaults.h"
|
||||
#include "esp_openthread_lock.h"
|
||||
#include "esp_openthread_netif_glue.h"
|
||||
#include "esp_openthread_types.h"
|
||||
#include "esp_vfs_eventfd.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "protocol_examples_common.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "driver/uart.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "freertos/task.h"
|
||||
#include "hal/uart_types.h"
|
||||
#include "openthread/border_router.h"
|
||||
#include "openthread/cli.h"
|
||||
#include "openthread/dataset.h"
|
||||
#include "openthread/dataset_ftd.h"
|
||||
#include "openthread/dataset_updater.h"
|
||||
#include "openthread/error.h"
|
||||
#include "openthread/instance.h"
|
||||
#include "openthread/ip6.h"
|
||||
#include "openthread/tasklet.h"
|
||||
#include "openthread/thread.h"
|
||||
#include "openthread/thread_ftd.h"
|
||||
|
||||
#define TAG "esp_ot_br"
|
||||
|
||||
static int hex_digit_to_int(char hex)
|
||||
{
|
||||
if ('A' <= hex && hex <= 'F') {
|
||||
return 10 + hex - 'A';
|
||||
}
|
||||
if ('a' <= hex && hex <= 'f') {
|
||||
return 10 + hex - 'a';
|
||||
}
|
||||
if ('0' <= hex && hex <= '9') {
|
||||
return hex - '0';
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static size_t hex_string_to_binary(const char *hex_string, uint8_t *buf, size_t buf_size)
|
||||
{
|
||||
int num_char = strlen(hex_string);
|
||||
|
||||
if (num_char != buf_size * 2) {
|
||||
return 0;
|
||||
}
|
||||
for (size_t i = 0; i < num_char; i += 2) {
|
||||
int digit0 = hex_digit_to_int(hex_string[i]);
|
||||
int digit1 = hex_digit_to_int(hex_string[i + 1]);
|
||||
|
||||
if (digit0 < 0 || digit1 < 0) {
|
||||
return 0;
|
||||
}
|
||||
buf[i / 2] = (digit0 << 4) + digit1;
|
||||
}
|
||||
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
static void create_config_network(otInstance *instance)
|
||||
{
|
||||
otOperationalDataset dataset;
|
||||
uint16_t network_name_len = strnlen(CONFIG_OPENTHREAD_NETWORK_NAME, OT_NETWORK_NAME_MAX_SIZE + 1);
|
||||
|
||||
assert(network_name_len <= OT_NETWORK_NAME_MAX_SIZE);
|
||||
|
||||
if (otDatasetCreateNewNetwork(instance, &dataset) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to create OpenThread network dataset.");
|
||||
abort();
|
||||
}
|
||||
dataset.mChannel = CONFIG_OPENTHREAD_NETWORK_CHANNEL;
|
||||
dataset.mComponents.mIsChannelPresent = true;
|
||||
dataset.mPanId = CONFIG_OPENTHREAD_NETWORK_PANID;
|
||||
dataset.mComponents.mIsPanIdPresent = true;
|
||||
memcpy(dataset.mNetworkName.m8, CONFIG_OPENTHREAD_NETWORK_NAME, network_name_len);
|
||||
dataset.mComponents.mIsNetworkNamePresent = true;
|
||||
if (hex_string_to_binary(CONFIG_OPENTHREAD_NETWORK_EXTPANID, dataset.mExtendedPanId.m8,
|
||||
sizeof(dataset.mExtendedPanId.m8)) != sizeof(dataset.mExtendedPanId.m8)) {
|
||||
ESP_LOGE(TAG, "Cannot convert OpenThread extended pan id. Please double-check your config.");
|
||||
abort();
|
||||
}
|
||||
dataset.mComponents.mIsExtendedPanIdPresent = true;
|
||||
if (hex_string_to_binary(CONFIG_OPENTHREAD_NETWORK_MASTERKEY, dataset.mMasterKey.m8,
|
||||
sizeof(dataset.mMasterKey.m8)) != sizeof(dataset.mMasterKey.m8)) {
|
||||
ESP_LOGE(TAG, "Cannot convert OpenThread master key. Please double-check your config.");
|
||||
abort();
|
||||
}
|
||||
dataset.mComponents.mIsMasterKeyPresent = true;
|
||||
if (hex_string_to_binary(CONFIG_OPENTHREAD_NETWORK_PSKC, dataset.mPskc.m8, sizeof(dataset.mPskc.m8)) !=
|
||||
sizeof(dataset.mPskc.m8)) {
|
||||
ESP_LOGE(TAG, "Cannot convert OpenThread pre-shared commissioner key. Please double-check your config.");
|
||||
abort();
|
||||
}
|
||||
dataset.mComponents.mIsPskcPresent = true;
|
||||
if (otDatasetSetActive(instance, &dataset) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to set OpenThread active dataset.");
|
||||
abort();
|
||||
}
|
||||
if (otBorderRouterRegister(instance) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to register border router.");
|
||||
abort();
|
||||
}
|
||||
if (otIp6SetEnabled(instance, true) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to enable OpenThread IP6 link");
|
||||
abort();
|
||||
}
|
||||
if (otThreadSetEnabled(instance, true) != OT_ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Failed to enable OpenThread");
|
||||
abort();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void ot_task_worker(void *aContext)
|
||||
{
|
||||
esp_openthread_platform_config_t config = {
|
||||
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_UART_RCP_CONFIG(4, 5),
|
||||
.host_config = ESP_OPENTHREAD_DEFAULT_UART_HOST_CONFIG(),
|
||||
};
|
||||
|
||||
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
|
||||
esp_netif_t *openthread_netif = esp_netif_new(&cfg);
|
||||
assert(openthread_netif != NULL);
|
||||
|
||||
// Initialize the OpenThread stack
|
||||
ESP_ERROR_CHECK(esp_openthread_init(&config));
|
||||
|
||||
// Initialize border routing features
|
||||
ESP_ERROR_CHECK(esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init()));
|
||||
ESP_ERROR_CHECK(esp_openthread_border_router_init(get_example_netif()));
|
||||
|
||||
esp_openthread_lock_acquire(portMAX_DELAY);
|
||||
create_config_network(esp_openthread_get_instance());
|
||||
esp_openthread_lock_release();
|
||||
|
||||
// Run the main loop
|
||||
esp_openthread_launch_mainloop();
|
||||
|
||||
// Clean up
|
||||
esp_netif_destroy(openthread_netif);
|
||||
esp_openthread_netif_glue_deinit();
|
||||
esp_vfs_eventfd_unregister();
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Used eventfds:
|
||||
// * netif
|
||||
// * task queue
|
||||
esp_vfs_eventfd_config_t eventfd_config = {
|
||||
.max_fds = 2,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
|
||||
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
xTaskCreate(ot_task_worker, "ot_br_main", 20480, xTaskGetCurrentTaskHandle(), 5, NULL);
|
||||
}
|
6
examples/openthread/ot_br/partitions.csv
Normal file
6
examples/openthread/ot_br/partitions.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1200K,
|
||||
ot_storage, data, 0x3a, , 0x2000,
|
|
40
examples/openthread/ot_br/sdkconfig.defaults
Normal file
40
examples/openthread/ot_br/sdkconfig.defaults
Normal file
@ -0,0 +1,40 @@
|
||||
#
|
||||
# libsodium
|
||||
#
|
||||
CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y
|
||||
# end of libsodium
|
||||
|
||||
#
|
||||
# Partition Table
|
||||
#
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||
CONFIG_PARTITION_TABLE_MD5=y
|
||||
# end of Partition Table
|
||||
|
||||
#
|
||||
# mbedTLS
|
||||
#
|
||||
|
||||
CONFIG_MBEDTLS_CMAC_C=y
|
||||
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y
|
||||
# end of TLS Key Exchange Methods
|
||||
|
||||
CONFIG_MBEDTLS_ECJPAKE_C=y
|
||||
# end of mbedTLS
|
||||
|
||||
#
|
||||
# OpenThread
|
||||
#
|
||||
CONFIG_OPENTHREAD_ENABLED=y
|
||||
CONFIG_OPENTHREAD_BORDER_ROUTER=y
|
||||
# end of OpenThread
|
||||
|
||||
#
|
||||
# lwIP
|
||||
#
|
||||
CONFIG_LWIP_IPV6_NUM_ADDRESSES=8
|
||||
# end of lwIP
|
Loading…
Reference in New Issue
Block a user