samples: net: openthread: Add a sample using OpenThread CoAP API
In order to show how to use OpenThread and CoAP, add an application. This example could be build to run a client or server. The server could expose LEDs and buttons and client could get their state and set the LEDs state. The network is created automatically using the network key predefined in the config. The goal is to make the example simple by removing the commisionning and joinning process. If a client application has a button, it could use it to toggle the first LED on any boards running the server application. Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
This commit is contained in:
parent
0e743abb1d
commit
dc898b47c8
13 changed files with 1121 additions and 0 deletions
|
|
@ -13,4 +13,5 @@ supported:
|
|||
- spi
|
||||
- watchdog
|
||||
- hwinfo
|
||||
- netif:openthread
|
||||
vendor: ti
|
||||
|
|
|
|||
20
samples/net/openthread/coap/CMakeLists.txt
Normal file
20
samples/net/openthread/coap/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(ot_coap)
|
||||
|
||||
target_include_directories(app PRIVATE src)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/coap_utils.c
|
||||
)
|
||||
|
||||
target_sources_ifdef(CONFIG_OT_COAP_SAMPLE_LED app PRIVATE src/led.c)
|
||||
target_sources_ifdef(CONFIG_OT_COAP_SAMPLE_SW app PRIVATE src/button.c)
|
||||
|
||||
include(${ZEPHYR_BASE}/samples/net/common/common.cmake)
|
||||
|
||||
set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/)
|
||||
23
samples/net/openthread/coap/Kconfig
Normal file
23
samples/net/openthread/coap/Kconfig
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Config options for OpenThread Border CoAP sample app
|
||||
|
||||
# Copyright (c) 2024 Alexandre Bailon
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mainmenu "OpenThread CoAP Sample"
|
||||
|
||||
menu "Application configuration"
|
||||
|
||||
config OT_COAP_SAMPLE_SERVER
|
||||
bool "Build the sample CoAP server application"
|
||||
|
||||
config OT_COAP_SAMPLE_LED
|
||||
bool "Enable LED support"
|
||||
default y
|
||||
|
||||
config OT_COAP_SAMPLE_SW
|
||||
bool "Enable switch support"
|
||||
default y
|
||||
|
||||
endmenu
|
||||
|
||||
source "Kconfig.zephyr"
|
||||
178
samples/net/openthread/coap/README.rst
Normal file
178
samples/net/openthread/coap/README.rst
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
:orphan:
|
||||
|
||||
.. zephyr:code-sample:: ot-coap
|
||||
:name: OpenThread CoAP client and server application
|
||||
:relevant-api: openthread
|
||||
|
||||
Build a Full Thread Device (FTD) CoAP server and client.
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
This sample demonstrates how to use OpenThread CoAP API.
|
||||
It can be built to work as a server or as a client.
|
||||
|
||||
By running a client and server on two boards, a local Thread network can be created.
|
||||
To create the network, OpenThread uses the network key provided with Kconfig.
|
||||
Once the boards have been flashed, the network will be
|
||||
automatically created and configured.
|
||||
|
||||
Once the network is operational, then the client could start interacting with
|
||||
the server.
|
||||
Every time the user presses the button, the LED on server should toggle.
|
||||
|
||||
The source code for this sample application can be found at:
|
||||
:zephyr_file:`samples/net/openthread/coap`.
|
||||
|
||||
.. note::
|
||||
This sample uses the OpenThread CoAP API whereas Zephyr has its own CoAP API.
|
||||
So, why are we using the OpenThread CoAP API here ?
|
||||
|
||||
* OpenThread uses it internaly to implement many of its services.
|
||||
* OpenThread CoAP API has a more direct access to radio.
|
||||
|
||||
So by using OpenThread CoAP API instead of Zephyr one,
|
||||
we could expect less overhead although this makes the application less portable.
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
Build the OpenThread FTD CoAP server sample application like this:
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/net/openthread/coap
|
||||
:board: <board to use>
|
||||
:west-args: -T sample.net.openthread.ftd.coap.server
|
||||
:goals: build
|
||||
:compact:
|
||||
|
||||
Build the OpenThread FTD CoAP client sample application like this:
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/net/openthread/coap
|
||||
:board: <board to use>
|
||||
:west-args: -T sample.net.openthread.ftd.coap.client
|
||||
:goals: build
|
||||
:compact:
|
||||
|
||||
Example building CoAP server for the cc1352p7 launchpad:
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/net/openthread/coap
|
||||
:host-os: unix
|
||||
:board: cc1352p7_lp
|
||||
:west-args: -T sample.net.openthread.ftd.coap.server
|
||||
:goals: build flash
|
||||
:compact:
|
||||
|
||||
Example building CoAP client for the cc1352p7 launchpad:
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/net/openthread/coap
|
||||
:host-os: unix
|
||||
:board: cc1352p7_lp
|
||||
:west-args: -T sample.net.openthread.ftd.coap.client
|
||||
:goals: build flash
|
||||
:compact:
|
||||
|
||||
Checking Thread network state
|
||||
*****************************
|
||||
|
||||
Open a console on both server and client boards then check the sate:
|
||||
|
||||
.. code-block::
|
||||
|
||||
server:~$ ot state
|
||||
router
|
||||
Done
|
||||
|
||||
A valid state could be child, router or leader.
|
||||
|
||||
Once Thread network is operational, you could start using client.
|
||||
|
||||
Controlling server board's LED using a button on client board
|
||||
*************************************************************
|
||||
|
||||
There is nothing to do once Thread network is operational.
|
||||
Just press the button sw0 on the client and you should see led0 toggling.
|
||||
|
||||
The client uses a broadcast address to request CoAP server to toggle the LED.
|
||||
It does not know the address of the server so if there is a second server
|
||||
on the network, then the LED of the second board will toggle too.
|
||||
|
||||
Controlling server board's LED from a computer
|
||||
**********************************************
|
||||
|
||||
Although we use OpenThread CoAP API, we could interact with any CoAP client
|
||||
or server available on network. In this example, we are going to control the
|
||||
LED from a computer that is not in the Thread network.
|
||||
This requires an `OpenThread Border Router`_ with NAT64 support enabled on the same network.
|
||||
|
||||
First, check that the server (or the client) is connected to the otbr and
|
||||
can use NAT64:
|
||||
|
||||
.. code-block::
|
||||
|
||||
server:~$ ot netdata show
|
||||
router
|
||||
Done
|
||||
Prefixes:
|
||||
fd6f:cb3a:802:1::/64 paos low dc00
|
||||
Routes:
|
||||
fc00::/7 sa med dc00
|
||||
fd6f:cb3a:802:2:0:0::/96 sn low dc00
|
||||
Services:
|
||||
44970 01 14000500000e10 s dc00 0
|
||||
44970 5d fd78b9ce54779c6eb5484d062c3b5b22d120 s dc00 1
|
||||
Contexts:
|
||||
fd6f:cb3a:802:1::/64 1 c
|
||||
Commissioning:
|
||||
11426 - - -
|
||||
Done
|
||||
|
||||
Prefixes show the IPv6 prefies that could be used by device outside the
|
||||
Thread network to contact devices on Thread network.
|
||||
|
||||
We should have an IPv6 address using the prefix:
|
||||
|
||||
.. code-block::
|
||||
|
||||
server:~$ ot ipaddr
|
||||
fd78:b9ce:5477:9c6e:0:ff:fe00:a800
|
||||
fd6f:cb3a:802:1:f0ec:c1e2:c1bb:744
|
||||
fd78:b9ce:5477:9c6e:75b8:386c:1f79:1013
|
||||
fe80:0:0:0:50d1:bed5:6e6e:ad75
|
||||
Done
|
||||
|
||||
fd6f:cb3a:802:1:f0ec:c1e2:c1bb:744 is the IPv6 address that could be used
|
||||
to contact the CoAP server outside of the Thread network.
|
||||
|
||||
We could also check that we could access internet from Thread network:
|
||||
|
||||
.. code-block::
|
||||
|
||||
server:~$ ot ping 8.8.8.8
|
||||
Pinging synthesized IPv6 address: fd6f:cb3a:802:2:0:0:808:808
|
||||
16 bytes from fd6f:cb3a:802:2:0:0:808:808: icmp_seq=1 hlim=114 time=36ms
|
||||
1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 36/36.0/36 ms.
|
||||
Done
|
||||
|
||||
If everything is working, then, we could start controlling the LED from a computer.
|
||||
To do that, let's use aiocoap-client, a tool written in python.
|
||||
First, install it:
|
||||
|
||||
.. code-block::
|
||||
|
||||
pip install aiocoap
|
||||
|
||||
|
||||
Then, send a request to the server to toggle the LED:
|
||||
|
||||
.. code-block::
|
||||
|
||||
aiocoap-client -m PUT --payload '{"led_id":0,"state":2}' coap://[fd6f:cb3a:802:1:f0ec:c1e2:c1bb:744]/led
|
||||
|
||||
The LED state should have changed.
|
||||
|
||||
|
||||
.. _OpenThread Border Router: https://openthread.io/codelabs/openthread-border-router-nat64
|
||||
28
samples/net/openthread/coap/prj.conf
Normal file
28
samples/net/openthread/coap/prj.conf
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
CONFIG_NETWORKING=y
|
||||
CONFIG_NET_L2_OPENTHREAD=y
|
||||
CONFIG_OPENTHREAD_COAP=y
|
||||
CONFIG_OPENTHREAD_SLAAC=y
|
||||
CONFIG_JSON_LIBRARY=y
|
||||
|
||||
# Logging
|
||||
CONFIG_LOG=y
|
||||
CONFIG_LOG_MAX_LEVEL=1
|
||||
CONFIG_LOG_MODE_MINIMAL=n
|
||||
CONFIG_BOOT_BANNER=y
|
||||
CONFIG_LOG_BACKEND_UART=y
|
||||
|
||||
# Kernel options
|
||||
CONFIG_MAIN_STACK_SIZE=2560
|
||||
CONFIG_INIT_STACKS=y
|
||||
|
||||
# Add features required for FTD CLI
|
||||
CONFIG_SHELL=y
|
||||
CONFIG_OPENTHREAD_SHELL=y
|
||||
CONFIG_OPENTHREAD_PING_SENDER=y
|
||||
|
||||
# Network config
|
||||
CONFIG_OPENTHREAD_PANID=4660
|
||||
CONFIG_OPENTHREAD_XPANID="11:11:11:11:22:22:22:22"
|
||||
CONFIG_OPENTHREAD_NETWORKKEY="00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff"
|
||||
CONFIG_OPENTHREAD_CHANNEL=15
|
||||
CONFIG_OPENTHREAD_NETWORK_NAME="OpenThreadDemo"
|
||||
21
samples/net/openthread/coap/sample.yaml
Normal file
21
samples/net/openthread/coap/sample.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
common:
|
||||
harness: net
|
||||
tags:
|
||||
- net
|
||||
- openthread
|
||||
depends_on: openthread
|
||||
min_flash: 140
|
||||
sample:
|
||||
description: Runs the OpenThread stack as FTD with CoAP
|
||||
name: OpenThread FTD CoAP
|
||||
tests:
|
||||
sample.net.openthread.ftd.coap.client:
|
||||
build_only: true
|
||||
platform_allow:
|
||||
- cc1352p7_lp
|
||||
sample.net.openthread.ftd.coap.server:
|
||||
build_only: true
|
||||
platform_allow:
|
||||
- cc1352p7_lp
|
||||
extra_configs:
|
||||
- CONFIG_OT_COAP_SAMPLE_SERVER=y
|
||||
180
samples/net/openthread/coap/src/button.c
Normal file
180
samples/net/openthread/coap/src/button.c
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Alexandre Bailon
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(coap);
|
||||
|
||||
#include "coap_utils.h"
|
||||
#include "button.h"
|
||||
|
||||
struct btn_rsc_data {
|
||||
const struct gpio_dt_spec gpio;
|
||||
};
|
||||
|
||||
struct btn_rsc_ctx {
|
||||
struct btn_rsc_data *btn;
|
||||
int count;
|
||||
};
|
||||
|
||||
static const struct json_obj_descr json_btn_state_descr[] = {
|
||||
JSON_OBJ_DESCR_PRIM(struct json_btn_state, btn_id, JSON_TOK_NUMBER),
|
||||
JSON_OBJ_DESCR_PRIM(struct json_btn_state, state, JSON_TOK_NUMBER),
|
||||
};
|
||||
|
||||
static const struct json_obj_descr json_btn_get_descr[] = {
|
||||
JSON_OBJ_DESCR_PRIM(struct json_btn_get, device_id, JSON_TOK_STRING),
|
||||
JSON_OBJ_DESCR_OBJ_ARRAY(struct json_btn_get, btns, JSON_MAX_BTN, count,
|
||||
json_btn_state_descr, ARRAY_SIZE(json_btn_state_descr)),
|
||||
};
|
||||
|
||||
static K_SEM_DEFINE(btn_get_sem, 0, 1);
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SERVER
|
||||
static int btn_handler_get(void *ctx, otMessage *msg, const otMessageInfo *msg_info)
|
||||
{
|
||||
uint8_t buf[COAP_MAX_BUF_SIZE];
|
||||
struct btn_rsc_ctx *btn_ctx = ctx;
|
||||
|
||||
struct json_btn_get btn_data = {
|
||||
.device_id = coap_device_id(),
|
||||
};
|
||||
|
||||
for (int i = 0; i < btn_ctx->count; i++) {
|
||||
btn_data.btns[i].btn_id = i;
|
||||
btn_data.btns[i].state = gpio_pin_get_dt(&btn_ctx->btn[i].gpio);
|
||||
}
|
||||
btn_data.count = btn_ctx->count;
|
||||
|
||||
json_obj_encode_buf(json_btn_get_descr, ARRAY_SIZE(json_btn_get_descr), &btn_data, buf,
|
||||
COAP_MAX_BUF_SIZE);
|
||||
|
||||
return coap_resp_send(msg, msg_info, buf, strlen(buf) + 1);
|
||||
}
|
||||
|
||||
static void btn_handler(void *ctx, otMessage *msg, const otMessageInfo *msg_info)
|
||||
{
|
||||
coap_req_handler(ctx, msg, msg_info, NULL, btn_handler_get);
|
||||
}
|
||||
|
||||
#define DEFINE_BTN_CTX(node_id) \
|
||||
{ \
|
||||
.gpio = GPIO_DT_SPEC_GET(node_id, gpios), \
|
||||
},
|
||||
|
||||
#define DEFINE_BTNS_CTX(inst, compat, ...) DT_FOREACH_CHILD(DT_INST(inst, compat), DEFINE_BTN_CTX)
|
||||
|
||||
static struct btn_rsc_data btn_rsc_data[] = {
|
||||
DT_COMPAT_FOREACH_STATUS_OKAY_VARGS(gpio_keys, DEFINE_BTNS_CTX)};
|
||||
|
||||
static struct btn_rsc_ctx btn_rsc_ctx = {
|
||||
.btn = btn_rsc_data,
|
||||
.count = ARRAY_SIZE(btn_rsc_data),
|
||||
};
|
||||
|
||||
static otCoapResource btn_rsc = {
|
||||
.mUriPath = BTN_URI,
|
||||
.mHandler = btn_handler,
|
||||
.mContext = &btn_rsc_ctx,
|
||||
.mNext = NULL,
|
||||
};
|
||||
|
||||
static int button_init_rsc(otCoapResource *rsc)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
struct btn_rsc_ctx *btn_ctx = rsc->mContext;
|
||||
const struct gpio_dt_spec *gpio;
|
||||
|
||||
LOG_INF("Initializing the buttons");
|
||||
for (int i = 0; i < btn_ctx->count; i++) {
|
||||
gpio = &btn_ctx->btn[i].gpio;
|
||||
ret = button_init(gpio);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void coap_btn_reg_rsc(void)
|
||||
{
|
||||
otInstance *ot = openthread_get_default_instance();
|
||||
|
||||
button_init_rsc(&btn_rsc);
|
||||
LOG_INF("Registering button rsc");
|
||||
otCoapAddResource(ot, &btn_rsc);
|
||||
}
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
|
||||
|
||||
int button_init(const struct gpio_dt_spec *gpio)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!gpio_is_ready_dt(gpio)) {
|
||||
LOG_ERR("Error: button device %s is not ready\n", gpio->port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(gpio, GPIO_INPUT);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Error %d: failed to configure %s pin %d\n", ret, gpio->port->name,
|
||||
gpio->pin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_pin_interrupt_configure_dt(gpio, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Error %d: failed to configure interrupt on %s pin %d\n", ret,
|
||||
gpio->port->name, gpio->pin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void coap_btn_get_state_cb(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
|
||||
otError error)
|
||||
{
|
||||
uint8_t buf[COAP_MAX_BUF_SIZE];
|
||||
int len = COAP_MAX_BUF_SIZE;
|
||||
struct json_btn_get *btn = (struct json_btn_get *)ctx;
|
||||
int ret;
|
||||
|
||||
ret = coap_get_data(msg, buf, &len);
|
||||
if (ret) {
|
||||
btn->count = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
json_obj_parse(buf, len, json_btn_get_descr, ARRAY_SIZE(json_btn_get_descr), btn);
|
||||
exit:
|
||||
k_sem_give(&btn_get_sem);
|
||||
}
|
||||
|
||||
int coap_btn_get_state(const char *addr, int btn_id, int *state)
|
||||
{
|
||||
struct json_btn_get btn;
|
||||
int ret;
|
||||
|
||||
ret = coap_get_req_send(addr, BTN_URI, NULL, 0, coap_btn_get_state_cb, &btn);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = k_sem_take(&btn_get_sem, K_FOREVER);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (btn_id >= btn.count) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*state = btn.btns[btn_id].state;
|
||||
return ret;
|
||||
}
|
||||
38
samples/net/openthread/coap/src/button.h
Normal file
38
samples/net/openthread/coap/src/button.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Alexandre Bailon
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef COAP_BUTTON_H
|
||||
#define COAP_BUTTON_H
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/data/json.h>
|
||||
|
||||
#define BTN_URI "sw"
|
||||
|
||||
#define BTN_MSG_STATE_OFF 0
|
||||
#define BTN_MSG_STATE_ON 1
|
||||
|
||||
#define JSON_MAX_BTN 4
|
||||
|
||||
struct json_btn_state {
|
||||
int btn_id;
|
||||
int state;
|
||||
};
|
||||
|
||||
struct json_btn_get {
|
||||
const char *device_id;
|
||||
struct json_btn_state btns[JSON_MAX_BTN];
|
||||
int count;
|
||||
};
|
||||
|
||||
int button_init(const struct gpio_dt_spec *gpio);
|
||||
int coap_btn_get_state(const char *addr, int led_id, int *state);
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SERVER
|
||||
void coap_btn_reg_rsc(void);
|
||||
#endif
|
||||
|
||||
#endif /* COAP_BUTTON_H */
|
||||
284
samples/net/openthread/coap/src/coap_utils.c
Normal file
284
samples/net/openthread/coap/src/coap_utils.c
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Alexandre Bailon
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(coap);
|
||||
|
||||
#include "coap_utils.h"
|
||||
|
||||
static uint8_t coap_buf[COAP_MAX_BUF_SIZE];
|
||||
static uint8_t coap_dev_id[COAP_DEVICE_ID_SIZE];
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SERVER
|
||||
static void coap_default_handler(void *context, otMessage *message,
|
||||
const otMessageInfo *message_info)
|
||||
{
|
||||
ARG_UNUSED(context);
|
||||
ARG_UNUSED(message);
|
||||
ARG_UNUSED(message_info);
|
||||
|
||||
LOG_INF("Received CoAP message that does not match any request "
|
||||
"or resource");
|
||||
}
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
|
||||
|
||||
static int coap_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
|
||||
otCoapResponseHandler handler, void *ctx, otCoapCode code)
|
||||
{
|
||||
otInstance *ot;
|
||||
otMessage *msg;
|
||||
otMessageInfo msg_info;
|
||||
otError err;
|
||||
int ret;
|
||||
|
||||
ot = openthread_get_default_instance();
|
||||
if (!ot) {
|
||||
LOG_ERR("Failed to get an OpenThread instance");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
memset(&msg_info, 0, sizeof(msg_info));
|
||||
otIp6AddressFromString(addr, &msg_info.mPeerAddr);
|
||||
msg_info.mPeerPort = OT_DEFAULT_COAP_PORT;
|
||||
|
||||
msg = otCoapNewMessage(ot, NULL);
|
||||
if (!msg) {
|
||||
LOG_ERR("Failed to allocate a new CoAP message");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
otCoapMessageInit(msg, OT_COAP_TYPE_CONFIRMABLE, code);
|
||||
|
||||
err = otCoapMessageAppendUriPathOptions(msg, uri);
|
||||
if (err != OT_ERROR_NONE) {
|
||||
LOG_ERR("Failed to append uri-path: %s", otThreadErrorToString(err));
|
||||
ret = -EBADMSG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = otCoapMessageSetPayloadMarker(msg);
|
||||
if (err != OT_ERROR_NONE) {
|
||||
LOG_ERR("Failed to set payload marker: %s", otThreadErrorToString(err));
|
||||
ret = -EBADMSG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = otMessageAppend(msg, buf, len);
|
||||
if (err != OT_ERROR_NONE) {
|
||||
LOG_ERR("Failed to set append payload to response: %s", otThreadErrorToString(err));
|
||||
ret = -EBADMSG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = otCoapSendRequest(ot, msg, &msg_info, handler, ctx);
|
||||
if (err != OT_ERROR_NONE) {
|
||||
LOG_ERR("Failed to send the request: %s", otThreadErrorToString(err));
|
||||
ret = -EIO; /* Find a better error code */
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
otMessageFree(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coap_put_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
|
||||
otCoapResponseHandler handler, void *ctx)
|
||||
{
|
||||
return coap_req_send(addr, uri, buf, len, handler, ctx, OT_COAP_CODE_PUT);
|
||||
}
|
||||
|
||||
int coap_get_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
|
||||
otCoapResponseHandler handler, void *ctx)
|
||||
{
|
||||
return coap_req_send(addr, uri, buf, len, handler, ctx, OT_COAP_CODE_GET);
|
||||
}
|
||||
|
||||
int coap_resp_send(otMessage *req, const otMessageInfo *req_info, uint8_t *buf, int len)
|
||||
{
|
||||
otInstance *ot;
|
||||
otMessage *resp;
|
||||
otCoapCode resp_code;
|
||||
otCoapType resp_type;
|
||||
otError err;
|
||||
int ret;
|
||||
|
||||
ot = openthread_get_default_instance();
|
||||
if (!ot) {
|
||||
LOG_ERR("Failed to get an OpenThread instance");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
resp = otCoapNewMessage(ot, NULL);
|
||||
if (!resp) {
|
||||
LOG_ERR("Failed to allocate a new CoAP message");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
switch (otCoapMessageGetType(req)) {
|
||||
case OT_COAP_TYPE_CONFIRMABLE:
|
||||
resp_type = OT_COAP_TYPE_ACKNOWLEDGMENT;
|
||||
break;
|
||||
case OT_COAP_TYPE_NON_CONFIRMABLE:
|
||||
resp_type = OT_COAP_TYPE_NON_CONFIRMABLE;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Invalid message type");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (otCoapMessageGetCode(req)) {
|
||||
case OT_COAP_CODE_GET:
|
||||
resp_code = OT_COAP_CODE_CONTENT;
|
||||
break;
|
||||
case OT_COAP_CODE_PUT:
|
||||
resp_code = OT_COAP_CODE_CHANGED;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Invalid message code");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = otCoapMessageInitResponse(resp, req, resp_type, resp_code);
|
||||
if (err != OT_ERROR_NONE) {
|
||||
LOG_ERR("Failed to initialize the response: %s", otThreadErrorToString(err));
|
||||
ret = -EBADMSG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = otCoapMessageSetPayloadMarker(resp);
|
||||
if (err != OT_ERROR_NONE) {
|
||||
LOG_ERR("Failed to set payload marker: %s", otThreadErrorToString(err));
|
||||
ret = -EBADMSG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = otMessageAppend(resp, buf, len);
|
||||
if (err != OT_ERROR_NONE) {
|
||||
LOG_ERR("Failed to set append payload to response: %s", otThreadErrorToString(err));
|
||||
ret = -EBADMSG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = otCoapSendResponse(ot, resp, req_info);
|
||||
if (err != OT_ERROR_NONE) {
|
||||
LOG_ERR("Failed to send the response: %s", otThreadErrorToString(err));
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
otMessageFree(resp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int coap_req_handler(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
|
||||
coap_req_handler_put put_fn, coap_req_handler_get get_fn)
|
||||
{
|
||||
otCoapCode msg_code = otCoapMessageGetCode(msg);
|
||||
otCoapType msg_type = otCoapMessageGetType(msg);
|
||||
int ret;
|
||||
|
||||
if (msg_type != OT_COAP_TYPE_CONFIRMABLE && msg_type != OT_COAP_TYPE_NON_CONFIRMABLE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (msg_code == OT_COAP_CODE_PUT && put_fn) {
|
||||
int len = otMessageGetLength(msg) - otMessageGetOffset(msg);
|
||||
|
||||
otMessageRead(msg, otMessageGetOffset(msg), coap_buf, len);
|
||||
ret = put_fn(ctx, coap_buf, len);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (msg_type == OT_COAP_TYPE_CONFIRMABLE) {
|
||||
ret = get_fn(ctx, msg, msg_info);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (msg_code == OT_COAP_CODE_GET) {
|
||||
return get_fn(ctx, msg, msg_info);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const char *coap_device_id(void)
|
||||
{
|
||||
otInstance *ot = openthread_get_default_instance();
|
||||
otExtAddress eui64;
|
||||
int i;
|
||||
|
||||
if (coap_dev_id[0] != '\0') {
|
||||
return coap_dev_id;
|
||||
}
|
||||
|
||||
otPlatRadioGetIeeeEui64(ot, eui64.m8);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (i * 2 >= COAP_DEVICE_ID_SIZE) {
|
||||
i = COAP_DEVICE_ID_SIZE - 1;
|
||||
break;
|
||||
}
|
||||
sprintf(coap_dev_id + i * 2, "%02x", eui64.m8[i]);
|
||||
}
|
||||
coap_dev_id[i * 2] = '\0';
|
||||
|
||||
return coap_dev_id;
|
||||
}
|
||||
|
||||
int coap_get_data(otMessage *msg, void *buf, int *len)
|
||||
{
|
||||
int coap_len = otMessageGetLength(msg) - otMessageGetOffset(msg);
|
||||
|
||||
if (coap_len > *len) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*len = coap_len;
|
||||
otMessageRead(msg, otMessageGetOffset(msg), buf, coap_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coap_init(void)
|
||||
{
|
||||
otError err;
|
||||
otInstance *ot;
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SERVER
|
||||
LOG_INF("Initializing OpenThread CoAP server");
|
||||
#else /* CONFIG_OT_COAP_SAMPLE_SERVER */
|
||||
LOG_INF("Initializing OpenThread CoAP client");
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
|
||||
ot = openthread_get_default_instance();
|
||||
if (!ot) {
|
||||
LOG_ERR("Failed to get an OpenThread instance");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SERVER
|
||||
otCoapSetDefaultHandler(ot, coap_default_handler, NULL);
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
|
||||
|
||||
err = otCoapStart(ot, OT_DEFAULT_COAP_PORT);
|
||||
if (err != OT_ERROR_NONE) {
|
||||
LOG_ERR("Cannot start CoAP: %s", otThreadErrorToString(err));
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
30
samples/net/openthread/coap/src/coap_utils.h
Normal file
30
samples/net/openthread/coap/src/coap_utils.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Alexandre Bailon
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef COAP_UTILS_H
|
||||
#define COAP_UTILS_H
|
||||
|
||||
#include <zephyr/net/openthread.h>
|
||||
#include <openthread/coap.h>
|
||||
|
||||
#define COAP_MAX_BUF_SIZE 128
|
||||
#define COAP_DEVICE_ID_SIZE 25
|
||||
|
||||
typedef int (*coap_req_handler_put)(void *ctx, uint8_t *buf, int size);
|
||||
typedef int (*coap_req_handler_get)(void *ctx, otMessage *msg, const otMessageInfo *msg_info);
|
||||
|
||||
int coap_init(void);
|
||||
int coap_req_handler(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
|
||||
coap_req_handler_put put_fn, coap_req_handler_get get_fn);
|
||||
int coap_resp_send(otMessage *req, const otMessageInfo *req_info, uint8_t *buf, int len);
|
||||
int coap_put_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
|
||||
otCoapResponseHandler handler, void *ctx);
|
||||
int coap_get_req_send(const char *addr, const char *uri, uint8_t *buf, int len,
|
||||
otCoapResponseHandler handler, void *ctx);
|
||||
const char *coap_device_id(void);
|
||||
int coap_get_data(otMessage *msg, void *buf, int *len);
|
||||
|
||||
#endif /* COAP_UTILS_H */
|
||||
218
samples/net/openthread/coap/src/led.c
Normal file
218
samples/net/openthread/coap/src/led.c
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Alexandre Bailon
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(coap);
|
||||
|
||||
#include "coap_utils.h"
|
||||
#include "led.h"
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SERVER
|
||||
struct led_rsc_data {
|
||||
const struct gpio_dt_spec gpio;
|
||||
int state;
|
||||
};
|
||||
|
||||
struct led_rsc_ctx {
|
||||
struct led_rsc_data *led;
|
||||
int count;
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct json_obj_descr json_led_state_descr[] = {
|
||||
JSON_OBJ_DESCR_PRIM(struct json_led_state, led_id, JSON_TOK_NUMBER),
|
||||
JSON_OBJ_DESCR_PRIM(struct json_led_state, state, JSON_TOK_NUMBER),
|
||||
};
|
||||
|
||||
static const struct json_obj_descr json_led_get_descr[] = {
|
||||
JSON_OBJ_DESCR_PRIM(struct json_led_get, device_id, JSON_TOK_STRING),
|
||||
JSON_OBJ_DESCR_OBJ_ARRAY(struct json_led_get, leds, JSON_MAX_LED, count,
|
||||
json_led_state_descr, ARRAY_SIZE(json_led_state_descr)),
|
||||
};
|
||||
|
||||
K_SEM_DEFINE(led_get_sem, 0, 1);
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SERVER
|
||||
static int led_init(otCoapResource *rsc)
|
||||
{
|
||||
struct led_rsc_ctx *led_ctx = rsc->mContext;
|
||||
int ret;
|
||||
|
||||
LOG_INF("Initializing the LED");
|
||||
for (int i = 0; i < led_ctx->count; i++) {
|
||||
struct led_rsc_data *led = &led_ctx->led[i];
|
||||
|
||||
if (!gpio_is_ready_dt(&led->gpio)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&led->gpio, GPIO_OUTPUT);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to configure the GPIO");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int led_handler_put(void *ctx, uint8_t *buf, int size)
|
||||
{
|
||||
struct json_led_state led_data;
|
||||
struct led_rsc_ctx *led_ctx = ctx;
|
||||
struct led_rsc_data *led;
|
||||
int ret = -EINVAL;
|
||||
|
||||
json_obj_parse(buf, size, json_led_state_descr, ARRAY_SIZE(json_led_state_descr),
|
||||
&led_data);
|
||||
|
||||
if (led_data.led_id >= led_ctx->count) {
|
||||
LOG_ERR("Invalid led id: %x", led_data.led_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
led = &led_ctx->led[led_data.led_id];
|
||||
|
||||
switch (led_data.state) {
|
||||
case LED_MSG_STATE_ON:
|
||||
ret = gpio_pin_set_dt(&led->gpio, 1);
|
||||
led->state = 1;
|
||||
break;
|
||||
case LED_MSG_STATE_OFF:
|
||||
ret = gpio_pin_set_dt(&led->gpio, 0);
|
||||
led->state = 0;
|
||||
break;
|
||||
case LED_MSG_STATE_TOGGLE:
|
||||
led->state = 1 - led->state;
|
||||
ret = gpio_pin_set_dt(&led->gpio, led->state);
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Set an unsupported LED state: %x", led_data.state);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int led_handler_get(void *ctx, otMessage *msg, const otMessageInfo *msg_info)
|
||||
{
|
||||
uint8_t buf[COAP_MAX_BUF_SIZE];
|
||||
struct led_rsc_ctx *led_ctx = ctx;
|
||||
|
||||
struct json_led_get led_data = {
|
||||
.device_id = coap_device_id(),
|
||||
};
|
||||
|
||||
for (int i = 0; i < led_ctx->count; i++) {
|
||||
led_data.leds[i].led_id = i;
|
||||
led_data.leds[i].state = led_ctx->led[i].state;
|
||||
}
|
||||
led_data.count = led_ctx->count;
|
||||
|
||||
json_obj_encode_buf(json_led_get_descr, ARRAY_SIZE(json_led_get_descr), &led_data, buf,
|
||||
COAP_MAX_BUF_SIZE);
|
||||
|
||||
return coap_resp_send(msg, msg_info, buf, strlen(buf) + 1);
|
||||
}
|
||||
|
||||
static void led_handler(void *ctx, otMessage *msg, const otMessageInfo *msg_info)
|
||||
{
|
||||
coap_req_handler(ctx, msg, msg_info, led_handler_put, led_handler_get);
|
||||
}
|
||||
|
||||
#define DEFINE_LED_CTX(node_id) \
|
||||
{ \
|
||||
.gpio = GPIO_DT_SPEC_GET(node_id, gpios), \
|
||||
.state = 0, \
|
||||
},
|
||||
|
||||
#define DEFINE_LEDS_CTX(inst, compat, ...) DT_FOREACH_CHILD(DT_INST(inst, compat), DEFINE_LED_CTX)
|
||||
|
||||
static struct led_rsc_data led_rsc_data[] = {
|
||||
DT_COMPAT_FOREACH_STATUS_OKAY_VARGS(gpio_leds, DEFINE_LEDS_CTX)};
|
||||
|
||||
static struct led_rsc_ctx led_rsc_ctx = {
|
||||
.led = led_rsc_data,
|
||||
.count = ARRAY_SIZE(led_rsc_data),
|
||||
};
|
||||
|
||||
static otCoapResource led_rsc = {
|
||||
.mUriPath = LED_URI,
|
||||
.mHandler = led_handler,
|
||||
.mContext = &led_rsc_ctx,
|
||||
.mNext = NULL,
|
||||
};
|
||||
|
||||
void coap_led_reg_rsc(void)
|
||||
{
|
||||
otInstance *ot = openthread_get_default_instance();
|
||||
|
||||
LOG_INF("Registering LED rsc");
|
||||
led_init(&led_rsc);
|
||||
otCoapAddResource(ot, &led_rsc);
|
||||
}
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
|
||||
|
||||
static void coap_led_send_req_cb(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
|
||||
otError error)
|
||||
{
|
||||
}
|
||||
|
||||
int coap_led_set_state(const char *addr, int led_id, int state)
|
||||
{
|
||||
uint8_t buf[COAP_MAX_BUF_SIZE];
|
||||
|
||||
struct json_led_state led_data = {
|
||||
.led_id = led_id,
|
||||
.state = state,
|
||||
};
|
||||
|
||||
json_obj_encode_buf(json_led_state_descr, ARRAY_SIZE(json_led_state_descr), &led_data, buf,
|
||||
COAP_MAX_BUF_SIZE);
|
||||
|
||||
return coap_put_req_send(addr, LED_URI, buf, strlen(buf) + 1, coap_led_send_req_cb, NULL);
|
||||
}
|
||||
|
||||
static void coap_led_get_state_cb(void *ctx, otMessage *msg, const otMessageInfo *msg_info,
|
||||
otError error)
|
||||
{
|
||||
uint8_t buf[COAP_MAX_BUF_SIZE];
|
||||
int len = COAP_MAX_BUF_SIZE;
|
||||
struct json_led_get *led = (struct json_led_get *)ctx;
|
||||
int ret;
|
||||
|
||||
ret = coap_get_data(msg, buf, &len);
|
||||
if (ret) {
|
||||
led->count = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
json_obj_parse(buf, len, json_led_get_descr, ARRAY_SIZE(json_led_get_descr), led);
|
||||
exit:
|
||||
k_sem_give(&led_get_sem);
|
||||
}
|
||||
|
||||
int coap_led_get_state(const char *addr, int led_id, int *state)
|
||||
{
|
||||
struct json_led_get led;
|
||||
int ret;
|
||||
|
||||
ret = coap_get_req_send(addr, LED_URI, NULL, 0, coap_led_get_state_cb, &led);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = k_sem_take(&led_get_sem, K_FOREVER);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (led_id >= led.count) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*state = led.leds[led_id].state;
|
||||
return ret;
|
||||
}
|
||||
38
samples/net/openthread/coap/src/led.h
Normal file
38
samples/net/openthread/coap/src/led.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Alexandre Bailon
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef COAP_LED_H
|
||||
#define COAP_LED_H
|
||||
|
||||
#include <zephyr/data/json.h>
|
||||
|
||||
#define LED_URI "led"
|
||||
|
||||
#define LED_MSG_STATE_OFF 0
|
||||
#define LED_MSG_STATE_ON 1
|
||||
#define LED_MSG_STATE_TOGGLE 2
|
||||
|
||||
#define JSON_MAX_LED 4
|
||||
|
||||
struct json_led_state {
|
||||
int led_id;
|
||||
int state;
|
||||
};
|
||||
|
||||
struct json_led_get {
|
||||
const char *device_id;
|
||||
struct json_led_state leds[JSON_MAX_LED];
|
||||
int count;
|
||||
};
|
||||
|
||||
int coap_led_set_state(const char *addr, int led_id, int state);
|
||||
int coap_led_get_state(const char *addr, int led_id, int *state);
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SERVER
|
||||
void coap_led_reg_rsc(void);
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
|
||||
|
||||
#endif /* COAP_LED_H */
|
||||
62
samples/net/openthread/coap/src/main.c
Normal file
62
samples/net/openthread/coap/src/main.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Alexandre Bailon
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(coap);
|
||||
|
||||
#include <coap_utils.h>
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_LED
|
||||
#include "led.h"
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_LED */
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SW
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include "button.h"
|
||||
|
||||
/*
|
||||
* Get button configuration from the devicetree sw0 alias. This is mandatory.
|
||||
*/
|
||||
#define SW0_NODE DT_ALIAS(sw0)
|
||||
#if !DT_NODE_HAS_STATUS_OKAY(SW0_NODE)
|
||||
#error "Unsupported board: sw0 devicetree alias is not defined"
|
||||
#endif
|
||||
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
|
||||
static struct gpio_callback button_cb_data;
|
||||
|
||||
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
coap_led_set_state("ff03::1", 0, LED_MSG_STATE_TOGGLE);
|
||||
}
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_SW */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SERVER
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_LED
|
||||
coap_led_reg_rsc();
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_LED */
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SW
|
||||
coap_btn_reg_rsc();
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_SW */
|
||||
#endif /* CONFIG_OT_COAP_SAMPLE_SERVER */
|
||||
|
||||
ret = coap_init();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OT_COAP_SAMPLE_SW
|
||||
button_init(&button);
|
||||
|
||||
gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
|
||||
gpio_add_callback_dt(&button, &button_cb_data);
|
||||
#endif /*CONFIG_OT_COAP_SAMPLE_SW */
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in a new issue