Hello Nordic forum,
I must not quite understand how CoAP callbacks should work. Would anyone be able to give me any advice as to why my modified coap_client results in undefined behavior? In my main program, I'd call the function coap_send() at various points while varying the tx_payload string.
#include "coap.h"
#include <stdio.h>
#include <string.h>
#if defined(CONFIG_POSIX_API)
#include <zephyr/posix/arpa/inet.h>
#include <zephyr/posix/netdb.h>
#include <zephyr/posix/sys/socket.h>
#include <zephyr/posix/poll.h>
#else
#include <zephyr/net/socket.h>
#endif /* CONFIG_POSIX_API */
#include <zephyr/kernel.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/net/coap.h>
#include <zephyr/net/socket.h>
#include <zephyr/net/conn_mgr_connectivity.h>
#include <zephyr/net/conn_mgr_monitor.h>
#include <zephyr/random/random.h>
#include <zephyr/net/coap_client.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_ctrl.h>
#include <modem/lte_lc.h>
LOG_MODULE_REGISTER(coap_client_sample, CONFIG_COAP_CLIENT_SAMPLE_LOG_LEVEL);
/* Macros used to subscribe to specific Zephyr NET management events. */
#define L4_EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED)
#define CONN_LAYER_EVENT_MASK (NET_EVENT_CONN_IF_FATAL_ERROR)
/* Macro called upon a fatal error, reboots the device. */
#define FATAL_ERROR() \
LOG_ERR("Fatal error! Rebooting the device."); \
LOG_PANIC(); \
IF_ENABLED(CONFIG_REBOOT, (sys_reboot(0)))
/* Zephyr NET management event callback structures. */
static struct net_mgmt_event_callback l4_cb;
static struct net_mgmt_event_callback conn_cb;
/* Variable used to indicate if network is connected. */
static bool is_connected;
/* Mutex and conditional variable used to signal network connectivity. */
K_MUTEX_DEFINE(network_connected_lock);
K_CONDVAR_DEFINE(network_connected);
static int server_resolve(struct sockaddr_storage *server)
{
int err;
struct addrinfo *result;
struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_DGRAM
};
char ipv4_addr[NET_IPV4_ADDR_LEN];
err = getaddrinfo(CONFIG_COAP_SAMPLE_SERVER_HOSTNAME, NULL, &hints, &result);
if (err) {
LOG_ERR("getaddrinfo, error: %d", err);
return err;
}
if (result == NULL) {
LOG_ERR("Address not found");
return -ENOENT;
}
/* IPv4 Address. */
struct sockaddr_in *server4 = ((struct sockaddr_in *)server);
server4->sin_addr.s_addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr;
server4->sin_family = AF_INET;
server4->sin_port = htons(CONFIG_COAP_SAMPLE_SERVER_PORT);
inet_ntop(AF_INET, &server4->sin_addr.s_addr, ipv4_addr, sizeof(ipv4_addr));
LOG_INF("IPv4 Address found %s", ipv4_addr);
/* Free the address. */
freeaddrinfo(result);
return 0;
}
static void wait_for_network(void)
{
k_mutex_lock(&network_connected_lock, K_FOREVER);
if (!is_connected) {
LOG_INF("Waiting for network connectivity");
k_condvar_wait(&network_connected, &network_connected_lock, K_FOREVER);
}
k_mutex_unlock(&network_connected_lock);
}
static void response_cb(int16_t code, size_t offset, const uint8_t *payload,
size_t len, bool last_block, void *user_data)
{
if (code >= 0) {
//LOG_INF("CoAP response code: 0x%x", code);
//Achim's suggestion.
LOG_INF("CoAP response: code: 0x%x, payload: %u bytes", code, len);
} else {
LOG_INF("Response received with error code: %d", code);
}
}
static int periodic_coap_request_loop(char* tx_payload)
{
size_t tx_payload_len = strlen(tx_payload);
int err, sock = 0;
struct sockaddr_storage server = { 0 };
struct coap_client coap_client = { 0 };
struct coap_client_request req = {
.method = COAP_METHOD_POST,
.confirmable = true,
.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
.payload = tx_payload,
.cb = response_cb,
.len = tx_payload_len,
.path = ""
};
err = server_resolve(&server);
if (err) {
LOG_ERR("Failed to resolve server name");
return err;
}
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
LOG_ERR("Failed to create CoAP socket: %d.", -errno);
return -errno;
}
else {
LOG_INF("Socket created");
}
static bool coap_client_init_done = false;
if (coap_client_init_done == false) {
LOG_INF("Initializing CoAP client");
err = coap_client_init(&coap_client, NULL);
if (err) {
LOG_ERR("Failed to initialize CoAP client: %d", err);
return err;
}
coap_client_init_done = true;
}
wait_for_network();
/* Send request */
err = coap_client_req(&coap_client, sock, (struct sockaddr *)&server, &req, NULL);
if (err) {
LOG_ERR("Failed to send request: %d", err);
return err;
}
LOG_INF("CoAP POST request sent sent to %s", CONFIG_COAP_SAMPLE_SERVER_HOSTNAME);
LOG_INF("Sent request, closing socket in 3 seconds");
k_sleep(K_SECONDS(3));
/* Close socket */
close(sock);
return 0;
}
static void l4_event_handler(struct net_mgmt_event_callback *cb,
uint32_t event,
struct net_if *iface)
{
switch (event) {
case NET_EVENT_L4_CONNECTED:
LOG_INF("Network connectivity established");
k_mutex_lock(&network_connected_lock, K_FOREVER);
is_connected = true;
k_condvar_signal(&network_connected);
k_mutex_unlock(&network_connected_lock);
break;
case NET_EVENT_L4_DISCONNECTED:
LOG_INF("Network connectivity lost");
k_mutex_lock(&network_connected_lock, K_FOREVER);
is_connected = false;
k_mutex_unlock(&network_connected_lock);
break;
default:
/* Don't care */
return;
}
}
static void connectivity_event_handler(struct net_mgmt_event_callback *cb,
uint32_t event,
struct net_if *iface)
{
if (event == NET_EVENT_CONN_IF_FATAL_ERROR) {
LOG_ERR("NET_EVENT_CONN_IF_FATAL_ERROR");
FATAL_ERROR();
return;
}
}
int coap_send(char* tx_payload)
{
int err;
LOG_INF("The CoAP client started.");
/* Setup handler for Zephyr NET Connection Manager events and Connectivity layer. */
net_mgmt_init_event_callback(&l4_cb, l4_event_handler, L4_EVENT_MASK);
net_mgmt_add_event_callback(&l4_cb);
net_mgmt_init_event_callback(&conn_cb, connectivity_event_handler, CONN_LAYER_EVENT_MASK);
net_mgmt_add_event_callback(&conn_cb);
/* Bring all network interfaces up.
* Wi-Fi or LTE depending on the board that the sample was built for.
*/
LOG_INF("Bringing network interface up and connecting to the network");
k_sleep(K_SECONDS(1));
err = conn_mgr_all_if_up(true);
if (err) {
LOG_ERR("conn_mgr_all_if_up, error: %d", err);
FATAL_ERROR();
return err;
}
err = conn_mgr_all_if_connect(true);
if (err) {
LOG_ERR("conn_mgr_all_if_connect, error: %d", err);
FATAL_ERROR();
return err;
}
/* Resend connection status if the sample is built for NATIVE_SIM.
* This is necessary because the network interface is automatically brought up
* at SYS_INIT() before main() is called.
* This means that NET_EVENT_L4_CONNECTED fires before the
* appropriate handler l4_event_handler() is registered.
*/
if (IS_ENABLED(CONFIG_BOARD_NATIVE_SIM)) {
conn_mgr_mon_resend_status();
}
wait_for_network();
k_sleep(K_SECONDS(1));
err = periodic_coap_request_loop(tx_payload);
if (err) {
LOG_ERR("periodic_coap_request_loop, error: %d", err);
FATAL_ERROR();
return err;
}
LOG_INF("periodic_coap_request_loop completed!");
return 0;
}
The first time coap_send() is called, everything works fine. Message gets sent and callback is received. The second time, however, the message gets sent (verified by looking at my endpoint), but I never get the LOG_INF notifying me of the server response.
When I build my program using the default optimizations, the program crashes without warning directly after sending the second message. Interestingly, when I build my program using the debugging optimization, the program will actually work and send messages correctly, but I will not receive a server response.
To me, this seems to indicate a problem with how I'm doing the response callback.