mbed 5.4 with sleep mode

Dependencies:  

Revision:
34:d6ce8f961b8b
diff -r e27f40fada64 -r d6ce8f961b8b Coap/coap_client.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Coap/coap_client.c	Fri Mar 24 05:42:34 2017 +0000
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (c) 2015 Keith Cullen.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ *  @file coap_client.c
+ *
+ *  @brief Source file for the FreeCoAP client library
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "coap_client.h"
+#include "main.h"
+
+#define COAP_CLIENT_ACK_TIMEOUT_SEC   2                                         /**< Minimum delay to wait before retransmitting a confirmable message */
+#define COAP_CLIENT_MAX_RETRANSMIT    4                                         /**< Maximum number of times a confirmable message can be retransmitted */
+#define COAP_CLIENT_RESP_TIMEOUT_SEC  30                                        /**< Maximum amount of time to wait for a response */
+
+/****************************************************************************************************
+ *                                           coap_client                                            *
+ ****************************************************************************************************/
+
+int coap_client_create(coap_client_t *client,
+                       const char *host,
+                       const char *port)
+{
+    strncpy(client->server_host, host, sizeof(client->server_host) - 1);
+    strncpy(client->server_port, port, sizeof(client->server_port) - 1);
+
+    printf("Connected to host %s and port %s", client->server_host, client->server_port);
+    return 0;
+}
+
+void coap_client_destroy(coap_client_t *client)
+{
+    memset(client, 0, sizeof(coap_client_t));
+}
+
+/**
+ *  @brief Send a message to the server
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] msg Pointer to a message structure
+ *
+ *  @returns Number of bytes sent or error code
+ *  @retval >0 Number of bytes sent
+ *  @retval <0 Error
+ */
+static int coap_client_send(coap_client_t *client, coap_msg_t *msg)
+{
+    int num = 0;
+    char buf[COAP_MSG_MAX_BUF_LEN] = {0};
+
+    num = coap_msg_format(msg, buf, sizeof(buf));
+    if (num < 0)
+    {
+        return num;
+    }
+    num = send(client->sd, buf, num);
+    if (num < 0)
+    {
+        printf("send error\n");
+        return -errno;
+    }
+
+    printf("Sent to host %s and port %s\n", client->server_host, client->server_port);
+    return num;
+}
+
+/**
+ *  @brief Handle a format error in a received message
+ *
+ *  Special handling for the case where a received
+ *  message could not be parsed due to a format error.
+ *  Extract enough information from the received message
+ *  to form a reset message.
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] buf Buffer containing the message
+ *  @param[in] len length of the buffer
+ */
+static void coap_client_handle_format_error(coap_client_t *client, char *buf, size_t len)
+{
+    coap_msg_t msg = {0};
+    unsigned msg_id = 0;
+    unsigned type = 0;
+    int ret = 0;
+
+    ret = coap_msg_parse_type_msg_id(buf, len, &type, &msg_id);
+    if ((ret == 0) && (type == COAP_MSG_CON))
+    {
+        coap_msg_create(&msg);
+        ret = coap_msg_set_type(&msg, COAP_MSG_RST);
+        if (ret < 0)
+        {
+            coap_msg_destroy(&msg);
+            return;
+        }
+        ret = coap_msg_set_msg_id(&msg, msg_id);
+        if (ret < 0)
+        {
+            coap_msg_destroy(&msg);
+            return;
+        }
+        coap_client_send(client, &msg);
+        coap_msg_destroy(&msg);
+    }
+}
+
+/**
+ *  @brief Receive a message from the server
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] msg Pointer to a message structure
+ *
+ *  @returns Number of bytes received or error code
+ *  @retval >0 Number of bytes received
+ *  @retval <0 Error
+ */
+static int coap_client_recv(coap_client_t *client, coap_msg_t *msg)
+{
+    int num = 0;
+    int ret = 0;
+    char buf[COAP_MSG_MAX_BUF_LEN] = {0};
+
+    num = recv(client->sd, buf, sizeof(buf));
+    if (num < 0)
+    {
+        return -errno;
+    }
+
+    ret = coap_msg_parse(msg, buf, num);
+    if (ret < 0)
+    {
+        if (ret == -EBADMSG)
+        {
+            coap_client_handle_format_error(client, buf, num);
+        }
+        return ret;
+    }
+    printf("Received from host %s and port %s\n", client->server_host, client->server_port);
+    return num;
+}
+
+/**
+ *  @brief Reject a received confirmable message
+ *
+ *  Send a reset message to the server.
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] msg Pointer to a message structure
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ *  @retval <0 Error
+ */
+static int coap_client_reject_con(coap_client_t *client, coap_msg_t *msg)
+{
+    coap_msg_t rej = {0};
+    int num = 0;
+    int ret = 0;
+
+    printf("Rejecting confirmable message from host %s and port %s\n", client->server_host, client->server_port);
+    coap_msg_create(&rej);
+    ret = coap_msg_set_type(&rej, COAP_MSG_RST);
+    if (ret < 0)
+    {
+        coap_msg_destroy(&rej);
+        return ret;
+    }
+    ret = coap_msg_set_msg_id(&rej, coap_msg_get_msg_id(msg));
+    if (ret < 0)
+    {
+        coap_msg_destroy(&rej);
+        return ret;
+    }
+    num = coap_client_send(client, &rej);
+    coap_msg_destroy(&rej);
+    if (num < 0)
+    {
+        return num;
+    }
+    return 0;
+}
+
+/**
+ *  @brief Reject a received non-confirmable message
+ *
+ *  @param[in] client Pointer to a client structure
+ *  @param[in] msg Pointer to a message structure
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ */
+static int coap_client_reject_non(coap_client_t *client, coap_msg_t *msg)
+{
+    printf("Rejecting non-confirmable message from host %s and port %s\n", client->server_host, client->server_port);
+    return 0;
+}
+
+/**
+ *  @brief Reject a received acknowledgement message
+ *
+ *  @param[in] client Pointer to a client structure
+ *  @param[in] msg Pointer to a message structure
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ */
+static int coap_client_reject_ack(coap_client_t *client, coap_msg_t *msg)
+{
+    printf("Rejecting acknowledgement message from host %s and port %s\n", client->server_host, client->server_port);
+    return 0;
+}
+
+/**
+ *  @brief Reject a received reset message
+ *
+ *  @param[in] client Pointer to a client structure
+ *  @param[in] msg Pointer to a message structure
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ */
+static int coap_client_reject_reset(coap_client_t *client, coap_msg_t *msg)
+{
+    printf("Rejecting reset message from host %s and port %s\n", client->server_host, client->server_port);
+    return 0;
+}
+
+/**
+ *  @brief Reject a received message
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] msg Pointer to a message structure
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ *  @retval <0 Error
+ */
+static int coap_client_reject(coap_client_t *client, coap_msg_t *msg)
+{
+    if (coap_msg_get_type(msg) == COAP_MSG_CON)
+    {
+        return coap_client_reject_con(client, msg);
+    }
+    else if (coap_msg_get_type(msg) == COAP_MSG_NON)
+    {
+        return coap_client_reject_non(client, msg);
+    }
+    else if (coap_msg_get_type(msg) == COAP_MSG_ACK)
+    {
+        return coap_client_reject_ack(client, msg);
+    }
+    else if (coap_msg_get_type(msg) == COAP_MSG_RST)
+    {
+        return coap_client_reject_reset(client, msg);
+    }
+    return 0;  /* should never arrive here */
+}
+
+/**
+ *  @brief Send an acknowledgement message to the server
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] msg Pointer to a message structure
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ *  @retval <0 Error
+ */
+static int coap_client_send_ack(coap_client_t *client, coap_msg_t *msg)
+{
+    coap_msg_t ack = {0};
+    int num = 0;
+    int ret = 0;
+
+    printf("Acknowledging confirmable message from host %s and port %s\n", client->server_host, client->server_port);
+    coap_msg_create(&ack);
+    ret = coap_msg_set_type(&ack, COAP_MSG_ACK);
+    if (ret < 0)
+    {
+        coap_msg_destroy(&ack);
+        return ret;
+    }
+    ret = coap_msg_set_msg_id(&ack, coap_msg_get_msg_id(msg));
+    if (ret < 0)
+    {
+        coap_msg_destroy(&ack);
+        return ret;
+    }
+    num = coap_client_send(client, &ack);
+    coap_msg_destroy(&ack);
+    if (num < 0)
+    {
+        return num;
+    }
+    return 0;
+}
+
+/**
+ *  @brief Compare the token values in a request message and a response message
+ *
+ *  @param[in] req Pointer to the request message
+ *  @param[in] resp Pointer to the response message
+ *
+ *  @returns Comparison value
+ *  @retval 0 the tokens are not equal
+ *  @retval 1 the tokens are equal
+ */
+static int coap_client_match_token(coap_msg_t *req, coap_msg_t *resp)
+{
+    return ((coap_msg_get_token_len(resp) == coap_msg_get_token_len(req))
+         && (memcmp(coap_msg_get_token(resp), coap_msg_get_token(req), coap_msg_get_token_len(req)) == 0));
+}
+
+/**
+ *  @brief Check that all of the options in a message are acceptable
+ *
+ *  For a proxy, options are acceptable if they are safe to forward or recognized or both.
+ *  For a server, options are acceptable if they are elective or recognized or both.
+ *
+ *  @param[in] msg Pointer to message structure
+ *
+ *  @returns Operation status or bad option number
+ *  @retval 0 Success
+ *  @retval >0 Bad option number
+ */
+static unsigned coap_client_check_options(coap_msg_t *msg)
+{
+#ifdef COAP_PROXY
+    return coap_msg_check_unsafe_ops(msg);
+#else  /* !COAP_PROXY */
+    return coap_msg_check_critical_ops(msg);
+#endif  /* COAP_PROXY */
+}
+
+/**
+ *  @brief Handle a received piggy-backed response message
+ *
+ *  An acknowledgement has been received that contains
+ *  the same token as the request. Check the response
+ *  contained within it.
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] resp Pointer to the response message
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ *  @retval <0 Error
+ */
+static int coap_client_handle_piggybacked_response(coap_client_t *client, coap_msg_t *resp)
+{
+    unsigned op_num = 0;
+
+    op_num = coap_client_check_options(resp);
+    if (op_num != 0)
+    {
+        printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port);
+        coap_client_reject(client, resp);
+        return -EBADMSG;
+    }
+    printf("Received acknowledgement and response from host %s and port %s\n", client->server_host, client->server_port);
+    return 0;
+}
+
+/**
+ *  @brief Handle a received separate response message
+ *
+ *  A separate response has been received that contains
+ *  the same token as the request. Check the response
+ *  and send an acknowledgement if necessary.
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] resp Pointer to the response message
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ *  @retval <0 Error
+ */
+static int coap_client_handle_sep_response(coap_client_t *client, coap_msg_t *resp)
+{
+    unsigned op_num = 0;
+
+    if (coap_msg_get_type(resp) == COAP_MSG_CON)
+    {
+        printf("Received confirmable response from host %s and port %s\n", client->server_host, client->server_port);
+        op_num = coap_client_check_options(resp);
+        if (op_num != 0)
+        {
+            printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port);
+            coap_client_reject(client, resp);
+            return -EBADMSG;
+        }
+        return coap_client_send_ack(client, resp);
+    }
+    else if (coap_msg_get_type(resp) == COAP_MSG_NON)
+    {
+        printf("Received non-confirmable response from host %s and port %s\n", client->server_host, client->server_port);
+        op_num = coap_client_check_options(resp);
+        if (op_num != 0)
+        {
+            printf("Found bad option number %u in message from host %s and port %s\n", op_num, client->server_host, client->server_port);
+            coap_client_reject(client, resp);
+            return -EBADMSG;
+        }
+        return 0;
+    }
+    coap_client_reject(client, resp);
+    return -EBADMSG;
+}
+
+/**
+ *  @brief Handle a separate response to a confirmable request
+ *
+ *  An acknowledgement has been received. Receive the
+ *  response and send an acknowledgement back to the server.
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] req Pointer to the request message
+ *  @param[out] resp Pointer to the response message
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ *  @retval <0 Error
+ */
+static int coap_client_exchange_sep(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp)
+{
+    int num = 0;
+    int ret = 0;
+
+    /* wait for a separate response to a confirmable request */
+    printf("Expecting response from host %s and port %s\n", client->server_host, client->server_port);
+    while (1)
+    {
+        int start_time = getseconds();
+        while(1)
+        {
+            num = coap_client_recv(client, resp);
+            if (num < 0)
+            {
+                return num;
+            }
+            else if(num > 0)
+            {
+                break;    
+            }
+            int stop_time = getseconds();
+            if((stop_time - start_time) > COAP_CLIENT_RESP_TIMEOUT_SEC)
+            {
+                printf("Timeout no data received\n");  
+                return -1;  
+            }
+        }
+        
+        if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req))
+        {
+            if (coap_msg_get_type(resp) == COAP_MSG_ACK)
+            {
+                /* message deduplication */
+                printf("Received duplicate acknowledgement from host %s and port %s\n", client->server_host, client->server_port);
+                continue;
+            }
+            else if (coap_msg_get_type(resp) == COAP_MSG_RST)
+            {
+                return -ECONNRESET;
+            }
+            coap_client_reject(client, resp);
+            return -EBADMSG;
+        }
+        if (coap_client_match_token(req, resp))
+        {
+             return coap_client_handle_sep_response(client, resp);
+        }
+        /* message deduplication */
+        /* we might have received a duplicate message that was already received from the same server */
+        /* reject the message and continue listening */
+        ret = coap_client_reject(client, resp);
+        if (ret < 0 )
+        {
+            return ret;
+        }
+    }
+    return 0;
+}
+
+/**
+ *  @brief Handle the response to a confirmable request
+ *
+ *  A confirmable request has been sent to the server.
+ *  Receive the acknowledgement and response. Send an
+ *  acknowledgement if necessary.
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] req Pointer to the request message
+ *  @param[out] resp Pointer to the response message
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ *  @retval <0 Error
+ */
+static int coap_client_exchange_con(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp)
+{
+    int num = 0;
+    int ret = 0;
+
+    /*  wait for piggy-backed response in ack message
+     *  or ack message and separate response message
+     */
+    printf("Expecting acknowledgement from host %s and port %s\n", client->server_host, client->server_port);
+    while (1)
+    {
+        int start_time = getseconds();
+        while(1)
+        {
+            num = coap_client_recv(client, resp);
+            if (num < 0)
+            {
+                return num;
+            }
+            else if(num > 0)
+            {
+                break;    
+            }
+            int stop_time = getseconds();
+            if((stop_time - start_time) > COAP_CLIENT_ACK_TIMEOUT_SEC)
+            {
+                printf("Timeout no data received\n");    
+                return -1;
+            }
+        }
+         
+        if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req))
+        {
+            if (coap_msg_get_type(resp) == COAP_MSG_ACK)
+            {
+                if (coap_msg_is_empty(resp))
+                {
+                    /* received ack message, wait for separate response message */
+                    printf("Received acknowledgement from host %s and port %s\n", client->server_host, client->server_port);
+                    return coap_client_exchange_sep(client, req, resp);
+                }
+                else if (coap_client_match_token(req, resp))
+                {
+                    return coap_client_handle_piggybacked_response(client, resp);
+                }
+            }
+            else if (coap_msg_get_type(resp) == COAP_MSG_RST)
+            {
+                return -ECONNRESET;
+            }
+            coap_client_reject(client, resp);
+            return -EBADMSG;
+        }
+        else if (coap_client_match_token(req, resp))
+        {
+            /* RFC7252
+             * as the underlying datagram transport may not be sequence-preserving,
+             * the Confirmable message carrying the response may actually arrive
+             * before or after the Acknowledgement message for the request; for
+             * the purposes of terminating the retransmission sequence, this also
+             * serves as an acknowledgement.
+             */
+             return coap_client_handle_sep_response(client, resp);
+        }
+        /* message deduplication */
+        /* we might have received a duplicate message that was already received from the same server */
+        /* reject the message and continue listening */
+        ret = coap_client_reject(client, resp);
+        if (ret < 0 )
+        {
+            return ret;
+        }
+    }
+    return 0;
+}
+
+/**
+ *  @brief Handle the response to a non-confirmable request
+ *
+ *  A non-confirmable request has been sent to the server.
+ *  Receive the response.
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] req Pointer to the request message
+ *  @param[out] resp Pointer to the response message
+ *
+ *  @returns Operation status
+ *  @retval 0 Success
+ *  @retval <0 Error
+ **/
+static int coap_client_exchange_non(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp)
+{
+    int num = 0;
+    int ret = 0;
+
+    printf("Expecting response from host %s and port %s\n", client->server_host, client->server_port);
+    while (1)
+    {
+        int start_time = getseconds();
+        while(1)
+        {
+            num = coap_client_recv(client, resp);
+            if (num < 0)
+            {
+                return num;
+            }
+            else if(num > 0)
+            {
+                break;    
+            }
+            int stop_time = getseconds();
+            if((stop_time - start_time) > COAP_CLIENT_RESP_TIMEOUT_SEC)
+            {
+                printf("Timeout no data received\n");    
+                return -1;
+            }
+        }
+ 
+        if (coap_msg_get_msg_id(resp) == coap_msg_get_msg_id(req))
+        {
+            if (coap_msg_get_type(resp) == COAP_MSG_RST)
+            {
+                return -ECONNRESET;
+            }
+            coap_client_reject(client, resp);
+            return -EBADMSG;
+        }
+        if (coap_client_match_token(req, resp))
+        {
+             return coap_client_handle_sep_response(client, resp);
+        }
+        /* message deduplication */
+        /* we might have received a duplicate message that was already received from the same server */
+        /* reject the message and continue listening */
+        ret = coap_client_reject(client, resp);
+        if (ret < 0 )
+        {
+            return ret;
+        }
+    }
+    return 0;
+}
+
+int coap_client_exchange(coap_client_t *client, coap_msg_t *req, coap_msg_t *resp)
+{
+    unsigned char msg_id_buf[2] = {0};
+    unsigned msg_id = 0;
+    int num = 0;
+    char token[4] = {0};
+    int ret = 0;
+
+    /* check for a valid request */
+    if ((coap_msg_get_type(req) == COAP_MSG_ACK)
+     || (coap_msg_get_type(req) == COAP_MSG_RST)
+     || (coap_msg_get_code_class(req) != COAP_MSG_REQ))
+    {
+        return -EINVAL;
+    }
+
+    /* generate the message ID */
+    coap_msg_gen_rand_str((char *)msg_id_buf, sizeof(msg_id_buf));
+    msg_id = (((unsigned)msg_id_buf[1]) << 8) | (unsigned)msg_id_buf[0];
+    ret = coap_msg_set_msg_id(req, msg_id);
+    if (ret < 0)
+    {
+        return ret;
+    }
+
+    /* generate the token */
+    coap_msg_gen_rand_str(token, sizeof(token));
+    ret = coap_msg_set_token(req, token, sizeof(token));
+    if (ret < 0)
+    {
+        return ret;
+    }
+
+    if (coap_msg_get_type(req) == COAP_MSG_CON)
+    {
+        printf("Sending confirmable request to host %s and port %s\n", client->server_host, client->server_port);
+    }
+    else if (coap_msg_get_type(req) == COAP_MSG_NON)
+    {
+        printf("Sending non-confirmable request to host %s and port %s\n", client->server_host, client->server_port);
+    }
+
+    num = coap_client_send(client, req);
+    if (num < 0)
+    {
+        return num;
+    }
+
+    if (coap_msg_get_type(req) == COAP_MSG_CON)
+    {
+        return coap_client_exchange_con(client, req, resp);
+    }
+    else if (coap_msg_get_type(req) == COAP_MSG_NON)
+    {
+        return coap_client_exchange_non(client, req, resp);
+    }
+    return -EINVAL;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ *  @brief Test an exchange with the server
+ *
+ *  @param[in] data Pointer to a client test data structure
+ *
+ *  @returns Test result
+ */
+test_result_t test_exchange_func(char* buf,int buf_len)
+{
+    printf("\n----------------------------------------\n");
+    test_result_t result = PASS;
+    coap_client_t client = {0};
+    coap_msg_t resp = {0};
+    coap_msg_t req = {0};
+    int ret = 0;
+
+    test_coap_client_msg_op_t req_ops;
+        req_ops.num = COAP_MSG_URI_PATH;
+        req_ops.len = 8;
+        req_ops.val = (char*)"resource";
+        
+    test_coap_client_msg_op_t resp_ops;
+        resp_ops.num = 0;
+        resp_ops.len = 0;
+        resp_ops.val = 0;
+
+    test_coap_client_msg_t test1_req;
+        test1_req.type = COAP_MSG_CON;
+        test1_req.code_class = COAP_MSG_REQ;
+        test1_req.code_detail = COAP_MSG_GET;
+        test1_req.ops = req_ops;
+        test1_req.num_ops = 1;
+        test1_req.payload = buf;
+        test1_req.payload_len = buf_len;
+
+    test_coap_client_msg_t test1_resp;
+        test1_resp.type = COAP_MSG_ACK;
+        test1_resp.code_class = COAP_MSG_SUCCESS;
+        test1_resp.code_detail = COAP_MSG_CONTENT;
+        test1_resp.ops = resp_ops;
+        test1_resp.num_ops = 0;
+        test1_resp.payload = (char*)"Hello Client!";
+        test1_resp.payload_len = 13;
+
+    test_coap_client_data_t test1_data; 
+        test1_data.desc = "test 1: send a confirmable request and expect a piggy-backed response";
+        test1_data.host = HOST;
+        test1_data.port = PORT;
+        test1_data.common_name = (char*)"dummy/server";
+        test1_data.test_req = test1_req;
+        test1_data.test_resp = test1_resp;
+        test1_data.num_msg = 1;
+
+    printf("%s\n", test1_data.desc);
+
+    ret = coap_client_create(&client,
+                             test1_data.host,
+                             test1_data.port);
+
+    if (ret < 0)
+    {
+        printf("Error  : %s\n",strerror(-ret));
+        return FAIL;
+    }
+
+        coap_msg_create(&req);
+        coap_msg_create(&resp);
+        
+        ret = exchange(&client, &test1_data.test_req, &req, &resp);
+        printf("exchange function\n");
+        if (ret != PASS)
+        {
+            printf("exchange function fail\n");
+            coap_msg_destroy(&resp);
+            coap_msg_destroy(&req);
+            coap_client_destroy(&client);
+            return FAIL;
+        }
+
+        ret = compare_ver_token(&req, &resp);
+        if (ret != PASS)
+        {
+            coap_msg_destroy(&resp);
+            coap_msg_destroy(&req);
+            coap_client_destroy(&client);
+            return FAIL;
+        }
+
+        ret = check_resp(&test1_data.test_resp, &resp);
+        if (ret != PASS)
+        {
+            coap_msg_destroy(&resp);
+            coap_msg_destroy(&req);
+            coap_client_destroy(&client);
+            return FAIL;
+        }
+
+    coap_msg_destroy(&resp);
+    coap_msg_destroy(&req);
+    coap_client_destroy(&client);
+
+    return result;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ *  @brief Print a CoAP message
+ *
+ *  @param[in] str String to be printed before the message
+ *  @param[in] msg Pointer to a message structure
+ */
+void print_coap_msg(const char *str, coap_msg_t *msg)
+{
+    coap_msg_op_t *op = NULL;
+    unsigned num = 0;
+    unsigned len = 0;
+    unsigned i = 0;
+    unsigned j = 0;
+    char *payload = NULL;
+    char *token = NULL;
+    char *val = NULL;
+
+    printf("%s\n", str);
+    printf("ver:         0x%02x\n", coap_msg_get_ver(msg));
+    printf("type:        0x%02x\n", coap_msg_get_type(msg));
+    printf("token_len:   %d\n", coap_msg_get_token_len(msg));
+    printf("code_class:  %d\n", coap_msg_get_code_class(msg));
+    printf("code_detail: %d\n", coap_msg_get_code_detail(msg));
+    printf("msg_id:      0x%04x\n", coap_msg_get_msg_id(msg));
+    printf("token:      ");
+    token = coap_msg_get_token(msg);
+    for (i = 0; i < coap_msg_get_token_len(msg); i++)
+    {
+        printf(" 0x%02x", (unsigned char)token[i]);
+    }
+    printf("\n");
+    op = coap_msg_get_first_op(msg);
+    while (op != NULL)
+    {
+        num = coap_msg_op_get_num(op);
+        len = coap_msg_op_get_len(op);
+        val = coap_msg_op_get_val(op);
+        printf("op[%u].num:   %u\n", j, num);
+        printf("op[%u].len:   %u\n", j, len);
+        printf("op[%u].val:  ", j);
+        for (i = 0; i < len; i++)
+        {
+            printf(" 0x%02x", (unsigned char)val[i]);
+        }
+        printf("\n");
+        op = coap_msg_op_get_next(op);
+        j++;
+    }
+    printf("payload:     ");
+    payload = coap_msg_get_payload(msg);
+    for (i = 0; i < coap_msg_get_payload_len(msg); i++)
+    {
+        printf("%c", payload[i]);
+    }
+    printf("\n");
+    printf("payload_len: %zu\n", coap_msg_get_payload_len(msg));
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ *  @brief Populate a request message with details from a test request message structure
+ *
+ *  @param[in] test_req Pointer to a test request message structure
+ *  @param[out] req Pointer to a request message structure
+ *
+ *  @returns Test result
+ */
+test_result_t populate_req(test_coap_client_msg_t *test_req, coap_msg_t *req)
+{
+    int ret = 0;
+
+    ret = coap_msg_set_type(req, test_req->type);
+    if (ret < 0)
+    {
+        printf("Error  : %s\n",strerror(-ret));
+        return FAIL;
+    }
+    ret = coap_msg_set_code(req, test_req->code_class, test_req->code_detail);
+    if (ret < 0)
+    {
+        printf("Error  : %s\n",strerror(-ret));
+        return FAIL;
+    }
+    ret = coap_msg_add_op(req, test_req->ops.num, test_req->ops.len, test_req->ops.val);
+    if (ret < 0)
+    {
+        printf("Error  : %s\n",strerror(-ret));
+        return FAIL;
+    }
+    if (test_req->payload)
+    {
+        ret = coap_msg_set_payload(req, test_req->payload, test_req->payload_len);
+        if (ret < 0)
+        {
+            printf("Error  : %s\n",strerror(-ret));
+            return FAIL;
+        }
+    }
+    return PASS;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ *  @brief Send a request to the server and receive the response
+ *
+ *  @param[in,out] client Pointer to a client structure
+ *  @param[in] test_req Pointer to a test request message structure
+ *  @param[out] req Pointer to a request message structure
+ *  @param[out] resp Pointer to a response message structure
+ *
+ *  @returns Test result
+ */
+test_result_t exchange(coap_client_t *client, test_coap_client_msg_t *test_req, coap_msg_t *req, coap_msg_t *resp)
+{
+    test_result_t result = PASS;
+    int ret = 0;
+
+    result = populate_req(test_req, req);
+    if (result != PASS)
+    {
+        printf("populate_req FAIL\n");
+        return result;
+    }
+    ret = coap_client_exchange(client, req, resp);
+    if (ret < 0)
+    {
+        printf("coap_client_exchange FAIL\n");
+        printf("Error  : %s\n",strerror(-ret));
+        return FAIL;
+    }
+    printf("coap_client_exchange PASS\n");
+    print_coap_msg("Sent:", req);
+    print_coap_msg("Received:", resp);
+
+    return PASS;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ *  @brief Compare the version and token fields in a request message and a response message
+ *
+ *  @param[out] req Pointer to a request message structure
+ *  @param[out] resp Pointer to a response message structure
+ *
+ *  @returns Test result
+ */
+test_result_t compare_ver_token(coap_msg_t *req, coap_msg_t *resp)
+{
+    if (coap_msg_get_ver(req) != coap_msg_get_ver(resp))
+    {
+        return FAIL;
+    }
+    if (coap_msg_get_token_len(req) != coap_msg_get_token_len(resp))
+    {
+        return FAIL;
+    }
+    else if (memcmp(coap_msg_get_token(req), coap_msg_get_token(resp), coap_msg_get_token_len(req)) != 0)
+    {
+        return FAIL;
+    }
+    return PASS;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+/**
+ *  @brief Check the fields in a response message against the expected values
+ *
+ *  @param[out] test_resp Pointer to a test response message structure
+ *  @param[out] resp Pointer to a response message structure
+ *
+ *  @returns Test result
+ */
+test_result_t check_resp(test_coap_client_msg_t *test_resp, coap_msg_t *resp)
+{
+    if (test_resp->type != coap_msg_get_type(resp))
+    {
+        return FAIL;
+    }
+    if (test_resp->code_class != coap_msg_get_code_class(resp))
+    {
+        return FAIL;
+    }
+    if (test_resp->code_detail != coap_msg_get_code_detail(resp))
+    {
+        return FAIL;
+    }
+    if (test_resp->payload_len != coap_msg_get_payload_len(resp))
+    {
+        return FAIL;
+    }
+    else if (memcmp(test_resp->payload, coap_msg_get_payload(resp), test_resp->payload_len))
+    {
+        return FAIL;
+    }
+    return PASS;
+}