emNet CoAP Client (Sample)

From SEGGER Knowledge Base
Jump to navigation Jump to search
IP_COAP_Client_Sample.c
Requires modifications Yes
Download IP_COAP_Client_Sample.c

This sample demonstrates how to start a CoAP client and send commands to a CoAP server.

NOTE: The CoAP server must be set via COAP_SERVER (and the port via COAP_PORT).

Code

/*********************************************************************
*                   (c) SEGGER Microcontroller GmbH                  *
*                        The Embedded Experts                        *
*                           www.segger.com                           *
**********************************************************************

-------------------------- END-OF-HEADER -----------------------------

Purpose : Sample program for embOS & emNet demonstrating a possible
          usage of the CoAP client.

Additional information:
  This sample starts a CoAP client and sends
  a couple of commands to the CoAP server specified
  via the "COAP_SERVER" and "COAP_PORT" defines.

  Example output:
    .
    .
    .
    4:076 MainTask - Test 0: Sending PING
    4:076 MainTask - Test 0: Success!
    4:077 MainTask - Test 1: Sending Discover
    4:098 MainTask - /obs: Simple observable data.
    /test: test entry
    /separate: simple variable with GET with LATE reply
    /BlockTransfer/ObsData: Observable data block.
    /ObsSensor:
    4:098 MainTask - Test 1: Success!
    4:099 MainTask - Test 2: Sending GET separate
    4:100 MainTask - 00:00:00
    4:100 MainTask - Test 2: Success!
    4:100 MainTask - Test 3: Sending NON GET block of 16 bytes
    4:101 MainTask - Test data
    4:101 MainTask - Test 3: Success!
    4:101 MainTask - Test 4: PUT test
    4:102 MainTask - Test 4: Success!
    4:103 MainTask - Test 5: PUT that should fail
    4:103 MainTask - received error: 4.05
    4:103 MainTask - Test 5: Success!
    4:103 MainTask - Test 6: DELETE test
    4:104 MainTask - Test 6: Success!
    4:104 MainTask - Test 7: POST test
    4:105 MainTask - Test 7: Success!
    4:105 MainTask - Test 8: Observe
    4:106 MainTask - Obs data
    4:106 MainTask - GET 1 from Observe completed
    .
    .
    .
    44:106 MainTask - Obs data
    44:106 MainTask - GET 5 from Observe completed
    44:106 MainTask - Test 8: Success!
    .
    .
    .
*/

#include "RTOS.h"
#include "BSP.h"
#include "IP.h"
#include "IP_COAP.h"
#include "SEGGER.h"

/*********************************************************************
*
*       Configuration
*
**********************************************************************
*/

#define USE_RX_TASK                0  // 0: Packets are read in ISR, 1: Packets are read in a task of its own.

//
// Task priorities.
//
enum {
   TASK_PRIO_IP_TASK = 150  // Priority should be higher than all IP application tasks.
#if USE_RX_TASK
  ,TASK_PRIO_IP_RX_TASK     // Must be the highest priority of all IP related tasks.
#endif
};

#define COAP_PORT     IP_COAP_DEFAULT_PORT
#define COAP_SERVER   "192.168.101.203"

//
// Application
//
#define DESCRIPTION_SIZE 2048
#define LAST_TEST        1000

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

static IP_HOOK_ON_STATE_CHANGE _StateChangeHook;
static int                     _IFaceId;

//
// Task stacks and Task-Control-Blocks.
//
static OS_STACKPTR int _IPStack[TASK_STACK_SIZE_IP_TASK/sizeof(int)];       // Stack of the IP_Task.
static OS_TASK         _IPTCB;                                              // Task-Control-Block of the IP_Task.

#if USE_RX_TASK
static OS_STACKPTR int _IPRxStack[TASK_STACK_SIZE_IP_RX_TASK/sizeof(int)];  // Stack of the IP_RxTask.
static OS_TASK         _IPRxTCB;                                            // Task-Control-Block of the IP_RxTask.
#endif

//
// CoAP contexts
//
static IP_COAP_CLIENT_CONTEXT  _COAPClient;
static IP_COAP_CONN_INFO       _ConnInfo;
static U32                     _MsgBuffer[1500 / sizeof(U32)];     // U32 to make sure we have a word alignment.

static IP_COAP_CLIENT_OBS      _Observer;

//
// Application's data
//
static int                     _TestId;
static int                     _CmdOnGoing;
static U8                      _CmdOnGoingIndex;
static int                     _ErrorDetected;
static int                     _NumObs;

static char                    _Description[DESCRIPTION_SIZE];
static U8                      _Payload[] = "<------0-------><------1------>\n<------2-------><------3------>\n";

static int                     _ObsOnGoing;
static char                    _ObsURI[] = "obs";

/*********************************************************************
*
*       Prototypes
*
**********************************************************************
*/

#ifdef __cplusplus
extern "C" {     /* Make sure we have C-declarations in C++ programs */
#endif
void MainTask(void);
#ifdef __cplusplus
}
#endif

/*********************************************************************
*
*       Local functions
*
**********************************************************************
*/

/*********************************************************************
*
*       _OnStateChange()
*
*  Function description
*    Callback that will be notified once the state of an interface
*    changes.
*
*  Parameters
*    IFaceId   : Zero-based interface index.
*    AdminState: Is this interface enabled ?
*    HWState   : Is this interface physically ready ?
*/
static void _OnStateChange(unsigned IFaceId, U8 AdminState, U8 HWState) {
  //
  // Check if this is a disconnect from the peer or a link down.
  // In this case call IP_Disconnect() to get into a known state.
  //
  if (((AdminState == IP_ADMIN_STATE_DOWN) && (HWState == 1)) ||  // Typical for dial-up connection e.g. PPP when closed from peer. Link up but app. closed.
      ((AdminState == IP_ADMIN_STATE_UP)   && (HWState == 0))) {  // Typical for any Ethernet connection e.g. PPPoE. App. opened but link down.
    IP_Disconnect(IFaceId);                                       // Disconnect the interface to a clean state.
  }
}


/*********************************************************************
*
*       Local functions - CoAP application example
*
**********************************************************************
*/

/*********************************************************************
*
*       _GET_Handler()
*
* Function description
*   Handles a GET.
*/
static int _GET_Handler(IP_COAP_CLIENT_CONTEXT* pContext, U8** ppPayload, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
  (void)pContext;
  (void)ppPayload;
  (void)pLength;
  (void)pParam;

  IP_COAP_APP_LOG(("\n  %.*s", *pLength, *ppPayload));
  return IP_COAP_RETURN_OK;
}

/*********************************************************************
*
*       _PUT_Handler()
*
* Function description
*   Handles a PUT / POST.
*/
static int _PUT_Handler(IP_COAP_CLIENT_CONTEXT* pContext, U8** ppPayload, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
  int Offset;
  U8* pBuf;
  U16 Length;
  U16 l;
  int r;
  int PayloadLength;

  (void)pContext;

  pBuf          = *ppPayload;
  Length        = *pLength;
  r             = IP_COAP_RETURN_SEND_END;          // By default, this is the last block.
  PayloadLength = IP_COAP_STRLEN((char*)_Payload);  // Don't count the 0 from the null-terminated string
  //
  Offset = pParam->pBlock->Index * pParam->pBlock->Size;
  if (Offset >= PayloadLength) {
    _ErrorDetected = 1;
    return IP_COAP_RETURN_NO_PAYLOAD;
  }
  //
  // Compute the remainging length of the payload not sent yet.
  //
  l = PayloadLength - Offset;
  //
  // If the remaining length of the payload is bigger than this
  // block size, reduce it to the block size.
  //
  if (l > Length) {
    r = IP_COAP_RETURN_SEND_BLOCK;  // More data to send in a next block.
    l = Length;
  }
  IP_COAP_MEMCPY(pBuf, _Payload + Offset, l);
  pBuf   += l;
  Length -= l;
  //
  *ppPayload = pBuf;
  *pLength   = Length;

  return r;
}

/*********************************************************************
*
*       _DISCOVER_Handler()
*
* Function description
*   Handles a GET .weel-known/core. Called for each block of data.
*   Stores the data in a static with the following format:
*   URI: description\n
*/
static int _DISCOVER_Handler(IP_COAP_CLIENT_CONTEXT* pContext, U8** ppPayload, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
  static int       InName;
  static int       InDesc;
         U8*       pBuf;
         U16       Length;
         unsigned  i;
         char      c;
  const  char      Title[] = "title=";
  static int       TitleCounter;
  static int       DescSize;
  static int       SkipFirst;

  (void)pContext;

  //
  // Reset the static on the first block and check the content format.
  //
  if (pParam->pBlock->Index == 0) {
    InName       = 0;
    InDesc       = 0;
    TitleCounter = 0;
    DescSize     = 0;
    SkipFirst    = 1;
    //
    // Check the content format (presence is mandatory).
    //
    if (IP_COAP_CheckContentFormat(pParam, IP_COAP_CT_LINK_FORMAT, 1) != IP_COAP_RETURN_OK) {
      IP_COAP_APP_LOG(("Test %d: Wrong content format!", _TestId));
      _ErrorDetected = 1;
    }
  }
  //
  pBuf   = *ppPayload;
  Length = *pLength;
  //
  for (i = 0; i < Length; i++) {
    c = *pBuf++;
    if (c == '<') {
      if (SkipFirst == 0) {
        if (DescSize < DESCRIPTION_SIZE) {
          _Description[DescSize++] = '\n';
        }
      }
      SkipFirst = 0;
      InName    = 1;
      continue;
    }
    if (c == '>') {
      InName = 0;
      if ((DescSize+2) < DESCRIPTION_SIZE) {
        _Description[DescSize++] = ':';
        _Description[DescSize++] = ' ';
      }
      continue;
    }
    if (c == '\"') {
      if ((InDesc == 0) && (TitleCounter == 6)) {
        InDesc = 1;
        continue;
      } else if (InDesc == 1) {
        InDesc = 0;
        continue;
      }
    }
    if (c == Title[TitleCounter]) {
      TitleCounter++;
    } else {
      TitleCounter = 0;
    }
    if (InName || InDesc) {
      if (c != '\n') {
        if (DescSize < DESCRIPTION_SIZE) {
          _Description[DescSize++] = c;
        }
      }
    }
  }
  //
  // Terminate the string with a 0.
  //
  _Description[DescSize] = 0;

  return IP_COAP_RETURN_OK;
}

/*********************************************************************
*
*       _HandleResult()
*
*  Function description
*    Print the result of the command.
*/
static void _HandleResult(int Index, U8 ResultCode, U8* pError, U16 ErrorLength) {

  //
  // In this example pError and ErrorLength are not used, but in some
  // error cases, it may contain a string with an explanation for the
  // error (to precise the error code).
  //
  (void)pError;
  (void)ErrorLength;

  //
  // Is this a reply corresponding to the sent request.
  //
  if (((_CmdOnGoing == 0) || (_CmdOnGoingIndex != Index)) && (_ObsOnGoing == 0)) {
    IP_COAP_APP_WARN(("Unrequested reply"));
    return;
  }
  _CmdOnGoing = 0;
  //
  switch (_TestId) {
    case 0:
      //
      // Expected reply: answer to a ping.
      //
      if (ResultCode == IP_COAP_CODE_EMPTY) {
        IP_COAP_APP_LOG(("Test %d: Success!", _TestId));
      } else {
        IP_COAP_APP_LOG(("Test %d: Error!!!", _TestId));
      }
      break;
    case 1:
      //
      // Expected answer to the discover.
      //
      if ((ResultCode == IP_COAP_CODE_SUCCESS_CONTENT) && (_ErrorDetected == 0)) {
        IP_COAP_APP_LOG(("%s", _Description));
        IP_COAP_APP_LOG(("Test %d: Success!", _TestId));
      } else {
        IP_COAP_APP_LOG(("Test %d: Error!!!", _TestId));
      }
      break;
    case 2:
      //
      // Expected answer to a GET separate.
      //
      if ((ResultCode == IP_COAP_CODE_SUCCESS_CONTENT) && (_ErrorDetected == 0)) {
        IP_COAP_APP_LOG(("Test %d: Success!", _TestId));
      } else {
        IP_COAP_APP_LOG(("Test %d: Error!!!", _TestId));
      }
      break;
    case 3:
      //
      // Expected answer to a GET with a NON.
      //
      if ((ResultCode == IP_COAP_CODE_SUCCESS_CONTENT) && (_ErrorDetected == 0)) {
        IP_COAP_APP_LOG(("Test %d: Success!", _TestId));
      } else {
        IP_COAP_APP_LOG(("Test %d: Error!!!", _TestId));
      }
      break;
    case 4:
      //
      // Expected answer to a PUT.
      //
      if ((ResultCode == IP_COAP_CODE_SUCCESS_CHANGED) && (_ErrorDetected == 0)) {
        IP_COAP_APP_LOG(("Test %d: Success!", _TestId));
      } else {
        IP_COAP_APP_LOG(("Test %d: Error!!!", _TestId));
      }
      break;
    case 5:
      //
      // Expected reject of a PUT.
      //
      if ((IP_COAP_GET_CLASS(ResultCode) != IP_COAP_CLASS_SUCCESS) && (_ErrorDetected == 0)) {
        IP_COAP_APP_LOG(("received error: %d.%02d", IP_COAP_GET_CLASS(ResultCode), ResultCode & 0x1F));
        IP_COAP_APP_LOG(("Test %d: Success!", _TestId));
      } else {
        IP_COAP_APP_LOG(("Test %d: Error!!!", _TestId));
      }
      break;
    case 6:
      //
      // Expected answer to a DELETED.
      //
      if ((ResultCode == IP_COAP_CODE_SUCCESS_DELETED) && (_ErrorDetected == 0)) {
        IP_COAP_APP_LOG(("Test %d: Success!", _TestId));
      } else {
        IP_COAP_APP_LOG(("Test %d: Error!!!", _TestId));
      }
      break;
    case 7:
      //
      // Expected answer to a PUT.
      //
      if (((ResultCode == IP_COAP_CODE_SUCCESS_CREATED) || (ResultCode == IP_COAP_CODE_SUCCESS_CHANGED)) && (_ErrorDetected == 0)) {
        IP_COAP_APP_LOG(("Test %d: Success!", _TestId));
      } else {
        IP_COAP_APP_LOG(("Test %d: Error!!!", _TestId));
      }
      break;
    case 8:
      //
      // End of an observable request.
      //
      _ObsOnGoing = 0;
      //
      if (_ErrorDetected == 0) {
        IP_COAP_APP_LOG(("Test %d: Success!", _TestId));
      } else {
        IP_COAP_APP_LOG(("Test %d: Error!!!", _TestId));
      }
      break;
    case 9:
      if (_ErrorDetected != 0) {
        IP_COAP_APP_LOG(("Error !!!"));
      }
      break;
    default:
      break;

  }
  _TestId++;
}

/*********************************************************************
*
*       _OBS_EndHandler()
*
* Function description
*   Indication of an observer end.
*/
static void _OBS_EndHandler(U8 ReturnCode, int IsFinal, void* pParam) {
  int r;
  U8  Index;

  Index = (U8)(U32)pParam;
  //
  if (ReturnCode != IP_COAP_CODE_SUCCESS_CONTENT) {
    _ErrorDetected = 1;
  }
  //
  // When the 5th GET is received, observation was cancelled, so
  // no more reply is expected.
  //
  if (_NumObs == 5) {
    _ErrorDetected = 1;
  }
  _NumObs++;
  //
  // IsFinal indicates the observation is aborted, either
  // the server replied to the GET but the value was not
  // observable or an error occured.
  //
  if (IsFinal != 0) {
    _ErrorDetected = 1;
    IP_COAP_APP_LOG(("\n  Observe terminated"));
  } else {
    IP_COAP_APP_LOG(("\n  GET %d from Observe completed", _NumObs));
    //
    // After 5 observation, cancel the observe request with an active abort.
    //
    if (_NumObs == 5) {
      //
      // Abort the observe.
      //
      IP_COAP_CLIENT_OBS_Abort(&_COAPClient, Index, &_Observer, 1);
      //
      // And actually send the abort reusing the on-going request.
      //
      IP_COAP_CLIENT_SetCommand(&_COAPClient, Index, IP_COAP_TYPE_CON, IP_COAP_CODE_REQ_GET);
      IP_COAP_CLIENT_SetOptionURIPath(&_COAPClient, Index, (U8*)&_ObsURI[0], (U8)IP_COAP_STRLEN(_ObsURI));
      //
      // Use the same payload handler as other GET.
      //
      IP_COAP_CLIENT_SetPayloadHandler(&_COAPClient, Index, _GET_Handler);
      //
      r = IP_COAP_CLIENT_BuildAndSend(&_COAPClient, Index);
      if (r != IP_COAP_RETURN_OK) {
        _ErrorDetected = 1;
        _TestId++;
      }
    }
  }
}

/*********************************************************************
*
*       _BuildNewRequest()
*
*  Function description
*    Sends a new request to the server.
*    Note that the following example shall be adapt depending
*    on the used CoAP server structure.
*/
static void _BuildNewRequest(unsigned Index) {
  IP_COAP_CLIENT_CONTEXT* pContext;
  int                     r;

  pContext       = &_COAPClient;
  _ErrorDetected = 0;
  //
  if (_CmdOnGoing != 0) {
    return;
  }
  //
  switch (_TestId) {
    case 0:
      //
      // First send a simple PING (CON, EMPTY) to the server.
      //
      IP_COAP_CLIENT_SetCommand(pContext, Index, IP_COAP_TYPE_CON, IP_COAP_CODE_EMPTY);
      //
      IP_COAP_CLIENT_BuildAndSend(pContext, Index);
      //
      _CmdOnGoing      = 1;
      _CmdOnGoingIndex = Index;
      IP_COAP_APP_LOG(("\nTest %d: Sending PING", _TestId));
      break;
    case 1:
      //
      // Change the default block size.
      //
      IP_COAP_CLIENT_SetDefaultBlockSize(&_COAPClient, 64);
      //
      // Send a "discover": GET on ".well-known/core"
      //
      IP_COAP_CLIENT_SetCommand(pContext, Index, IP_COAP_TYPE_CON, IP_COAP_CODE_REQ_GET);
      IP_COAP_CLIENT_SetOptionURIPath(pContext, Index, (U8*)".well-known/core", 16);
      IP_COAP_CLIENT_SetPayloadHandler(pContext, Index, _DISCOVER_Handler);
      //
      IP_COAP_CLIENT_BuildAndSend(pContext, Index);
      //
      _CmdOnGoing      = 1;
      _CmdOnGoingIndex = Index;
      IP_COAP_APP_LOG(("\nTest %d: Sending Discover", _TestId));
      break;
    case 2:
      //
      // Send a GET with a separate reply (no piggyback).
      //
      IP_COAP_CLIENT_SetCommand(pContext, Index, IP_COAP_TYPE_CON, IP_COAP_CODE_REQ_GET);
      IP_COAP_CLIENT_SetOptionURIPath(pContext, Index, (U8*)"separate", 8);
      IP_COAP_CLIENT_SetPayloadHandler(pContext, Index, _GET_Handler);
      //
      IP_COAP_CLIENT_BuildAndSend(pContext, Index);
      //
      _CmdOnGoing      = 1;
      _CmdOnGoingIndex = Index;
      IP_COAP_APP_LOG(("\nTest %d: Sending GET separate", _TestId));
      break;
    case 3:
      //
      // Send a GET test with a NON.
      //
      IP_COAP_CLIENT_SetCommand(pContext, Index, IP_COAP_TYPE_NON, IP_COAP_CODE_REQ_GET);
      //
      // Set a block size (will use a block of 16 for this request but
      // won't change the default of 64).
      //
      IP_COAP_CLIENT_SetOptionBlock(pContext, Index, 16);
      IP_COAP_CLIENT_SetOptionURIPath(pContext, Index, (U8*)"test", 4);
      IP_COAP_CLIENT_SetOptionAccept(pContext, Index, IP_COAP_CT_TXT);
      IP_COAP_CLIENT_SetPayloadHandler(pContext, Index, _GET_Handler);
      //
      IP_COAP_CLIENT_BuildAndSend(pContext, Index);
      //
      _CmdOnGoing      = 1;
      _CmdOnGoingIndex = Index;
      IP_COAP_APP_LOG(("\nTest %d: Sending NON GET block of 16 bytes", _TestId));
      //
      // Change the block size for the request
      //
      break;
    case 4:
      //
      // Change the default block size.
      //
      IP_COAP_CLIENT_SetDefaultBlockSize(&_COAPClient, 32);
      //
      // Send a PUT.
      //
      IP_COAP_CLIENT_SetCommand(pContext, Index, IP_COAP_TYPE_CON, IP_COAP_CODE_REQ_PUT);
      IP_COAP_CLIENT_SetOptionURIPath(pContext, Index, (U8*)"test", 4);
      IP_COAP_CLIENT_SetOptionSize1(pContext, Index, IP_COAP_STRLEN((char*)_Payload));
      IP_COAP_CLIENT_SetPayloadHandler(pContext, Index, _PUT_Handler);
      //
      IP_COAP_CLIENT_BuildAndSend(pContext, Index);
      //
      _CmdOnGoing      = 1;
      _CmdOnGoingIndex = Index;
      IP_COAP_APP_LOG(("\nTest %d: PUT test", _TestId));
      break;
    case 5:
      //
      // Send a PUT that will fail.
      //
      IP_COAP_CLIENT_SetCommand(pContext, Index, IP_COAP_TYPE_CON, IP_COAP_CODE_REQ_PUT);
      IP_COAP_CLIENT_SetOptionURIPath(pContext, Index, (U8*)"separate", 8);
      IP_COAP_CLIENT_SetOptionSize1(pContext, Index, sizeof((char*)_Payload));
      IP_COAP_CLIENT_SetPayloadHandler(pContext, Index, _PUT_Handler);
      //
      IP_COAP_CLIENT_BuildAndSend(pContext, Index);
      //
      _CmdOnGoing      = 1;
      _CmdOnGoingIndex = Index;
      IP_COAP_APP_LOG(("\nTest %d: PUT that should fail", _TestId));
      break;
    case 6:
      //
      // Send a DELETE.
      //
      IP_COAP_CLIENT_SetCommand(pContext, Index, IP_COAP_TYPE_CON, IP_COAP_CODE_REQ_DEL);
      IP_COAP_CLIENT_SetOptionURIPath(pContext, Index, (U8*)"test", 4);
      //
      IP_COAP_CLIENT_BuildAndSend(pContext, Index);
      //
      _CmdOnGoing      = 1;
      _CmdOnGoingIndex = Index;
      IP_COAP_APP_LOG(("\nTest %d: DELETE test", _TestId));
      break;
    case 7:
      //
      // Send a POST.
      //
      IP_COAP_CLIENT_SetCommand(pContext, Index, IP_COAP_TYPE_CON, IP_COAP_CODE_REQ_POST);
      IP_COAP_CLIENT_SetOptionURIPath(pContext, Index, (U8*)"test", 4);
      IP_COAP_CLIENT_SetOptionSize1(pContext, Index, sizeof((char*)_Payload));
      IP_COAP_CLIENT_SetOptionContentFormat(pContext, Index, IP_COAP_CT_TXT);
      IP_COAP_CLIENT_SetPayloadHandler(pContext, Index, _PUT_Handler);
      //
      IP_COAP_CLIENT_BuildAndSend(pContext, Index);
      //
      _CmdOnGoing      = 1;
      _CmdOnGoingIndex = Index;
      IP_COAP_APP_LOG(("\nTest %d: POST test", _TestId));
      break;
    case 8:
      //
      // Observable data.
      //
      if (_ObsOnGoing != 0) {
        break;
      }
      _NumObs = 0;
      //
      // Observe specific initialization.
      //
      IP_COAP_CLIENT_OBS_Init(pContext, Index, &_Observer, 1);
      IP_COAP_CLIENT_OBS_SetEndCallback(&_Observer, _OBS_EndHandler, (void*)(U32)Index);
      //
      // Regular request initialization. Observe uses a GET request.
      //
      IP_COAP_CLIENT_SetCommand(pContext, Index, IP_COAP_TYPE_CON, IP_COAP_CODE_REQ_GET);
      IP_COAP_CLIENT_SetOptionURIPath(pContext, Index, (U8*)&_ObsURI[0], (U8)IP_COAP_STRLEN(_ObsURI));
      IP_COAP_CLIENT_SetOptionBlock(pContext, Index, 16);
      //
      // Use the same payload handler as other GET.
      //
      IP_COAP_CLIENT_SetPayloadHandler(pContext, Index, _GET_Handler);
      //
      IP_COAP_APP_LOG(("\nTest %d: Observe", _TestId));
      //
      r = IP_COAP_CLIENT_BuildAndSend(pContext, Index);
      if (r != IP_COAP_RETURN_OK) {
        IP_COAP_APP_LOG(("Error starting the observer."));
        //
        // Go to next test.
        //
        _TestId++;
      } else {
        _ObsOnGoing = 1;
      }
      break;
    case 9:
      _TestId = LAST_TEST;
      break;
    default:
      break;
  }
}


/*********************************************************************
*
*       _RunApplication()
*
*  Function description
*    User interface handling.
*/
static int _RunApplication(void) {
  unsigned Index;
  U8       ResultCode;
  U8*      pError;
  U16      ErrorLength;
  int      r;

  //
  // Check if there is a pending result.
  //
  r = IP_COAP_CLIENT_GetLastResult(&_COAPClient, &ResultCode, &pError, &ErrorLength);
  if (r >= 0) {
    _HandleResult(r, ResultCode, pError, ErrorLength);
  }
  //
  // Check if there is a free request to execute a new one.
  //
  if (IP_COAP_CLIENT_GetFreeRequestIdx(&_COAPClient, &Index) == IP_COAP_RETURN_OK) {
    //
    // There is a free request: build a new request to be sent to the server.
    //
    _BuildNewRequest(Index);
  }
  //
  if (_TestId == LAST_TEST) {
    r = 1;
  } else {
    r = 0;
  }
  return r;
}

/*********************************************************************
*
*       _ConfigureClient()
*
*  Function description
*    Configures the application.
*/
static void _ConfigureClient(void) {
  //
  // Get server address.
  //
  IP_COAP_CLIENT_SetServerAddress(&_COAPClient, &_ConnInfo);
  //
  // Select with which test to start.
  //
  _TestId = 0;
}

/*********************************************************************
*
*       Local functions - CoAP functionality.
*
**********************************************************************
*/

/*********************************************************************
*
*       _IsIPAddress()
*
*  Function description
*    Checks if string is a dot-decimal IP address, for example 192.168.1.1
*/
static unsigned _IsIPAddress(const char* sIPAddr) {
  unsigned NumDots;
  unsigned i;
  char c;

  NumDots = 0;
  i       = 0;
  while (1) {
    c = *(sIPAddr + i);
    if ((c >= '0') && (c <= '9')) {
      goto Loop;
    }
    if (c == '.') {
      NumDots++;
      goto Loop;
    }
    if (c == '\0') {
      if ((NumDots < 3) || (i > 15)) { // Error, every dot-decimal IP address includes 3 '.' and is not longer as 15 characters.
Error:
        return 0;
      }
      return 1;
    } else {
      goto Error;
    }
Loop:
    i++;
  }
}

/*********************************************************************
*
*       _ParseIPAddr()
*
*  Function description
*    Parses a string for a dot-decimal IP address and returns the
*    IP as 32-bit number in host endianness.
*/
static long _ParseIPAddr(const char* sIPAddr) {
  long     IPAddr;
  unsigned Value;
  unsigned NumDots;
  unsigned i;
  unsigned j;
  char     acDigits[4];
  char     c;

  IPAddr = 0;
  //
  // Check if string is a valid IP address.
  //
  Value = _IsIPAddress(sIPAddr);
  if (Value) {
    //
    // Parse the IP address.
    //
    NumDots = 3;
    i       = 0;
    j       = 0;
    while (1) {
      c = *(sIPAddr + i);
      if (c == '\0') {
        //
        // Add the last byte of the IP address.
        //
        acDigits[j] = '\0';
        Value = SEGGER_atoi(acDigits);
        if (Value < 255) {
          IPAddr |= Value;
        }
        return IPAddr; // O.K., string completely parsed. Returning IP address.
      }
      //
      // Parse the first three byte of the IP address.
      //
      if (c != '.') {
        acDigits[j] = c;
        j++;
      } else {
        acDigits[j] = '\0';
        Value = SEGGER_atoi(acDigits);
        if (Value <= 255) {
          IPAddr |= (Value << (NumDots * 8));
          NumDots--;
          j = 0;
        } else {
          return -1;  // Error, illegal number in IP address.
        }
      }
      i++;
    }
  }
  return -1;
}

/*********************************************************************
*
*       _APP_Receive()
*
*  Function description
*    Receives a UDP packet.
*/
static int _APP_Receive(U8* pBuffer, unsigned BufferSize, IP_COAP_CONN_INFO* pInfo, unsigned* pIsMulticast) {
  fd_set              OriginalSocket;
  fd_set              OriginalStdin;
  fd_set              ReadFDS;
  fd_set              WriteFDS;
  int                 Receive;
  int                 SoError;
  unsigned            SocketFD;
  int                 AddressLength;
  struct sockaddr_in  ServerAddress;

  SocketFD = (int)pInfo->hSock;

  //
  // Clear the sets.
  //
  FD_ZERO(&OriginalSocket);
  FD_ZERO(&OriginalStdin);
  FD_ZERO(&ReadFDS);
  FD_ZERO(&WriteFDS);
  //
  // Add our descriptors to the set. (0 stands for stdin).
  FD_SET(SocketFD, &OriginalSocket);
  FD_SET(SocketFD, &ReadFDS);
  FD_SET(0,        &OriginalStdin);
  FD_SET(0,        &WriteFDS);
  //
  // wait until either socket has data ready to be recv() or timeout (0.5 secs).
  //
  ReadFDS  = OriginalSocket;
  WriteFDS = OriginalStdin;
  Receive  = select(&ReadFDS, NULL, NULL, 1);
  //
  if (Receive == -1) {
    IP_COAP_APP_WARN(("Select error"));
  } else if (Receive == 0) {
    //
    // Timeout.
    //
  } else {
    //
    // One or both of the descriptors have data.
    //
    if (FD_ISSET(SocketFD, &ReadFDS)) {
      FD_CLR(SocketFD, &ReadFDS);
      AddressLength = sizeof(ServerAddress);
      Receive       = recvfrom(SocketFD, (char*)pBuffer, BufferSize, 0,(struct sockaddr *)&ServerAddress, &AddressLength);
      if (Receive == SOCKET_ERROR) {
        getsockopt(SocketFD, SOL_SOCKET, SO_ERROR, &SoError, sizeof(SoError));
        IP_COAP_APP_WARN(("recvfrom failed with error: %s", IP_Err2Str(SoError)));
      }
    }
  }
  //
  // Always indicate it is not multicast/broadcast.
  //
  *pIsMulticast   = 0;

  return Receive;
}

/*********************************************************************
*
*       _APP_Send()
*
*  Function description
*    Receives a UDP packet.
*/
static int _APP_Send(U8* pBuffer, unsigned BufferSize, IP_COAP_CONN_INFO* pInfo) {
  struct sockaddr_in ClientAddress;
  int                AddressLength;
  int                r;

  ClientAddress.sin_addr.s_addr = htonl(pInfo->Addr.IPAddrV4);
  ClientAddress.sin_family      = AF_INET;
  ClientAddress.sin_port        = htons(pInfo->Port);
  //
  AddressLength = sizeof(struct sockaddr_in);
  //
  r = sendto((int)pInfo->hSock, (char*)pBuffer, BufferSize, 0, (struct sockaddr *)&ClientAddress, AddressLength);
  //
  if (r == -1) {
    IP_COAP_APP_WARN(("CoAP sending error\n"));
  }
  return r;
}

/*********************************************************************
*
*       _APP_GetTimeMs()
*
*  Function description
*    Returns the time in ms.
*
*  Important note
*    Windows time doesn't rollover on U32.
*
*/
static U32 _APP_GetTimeMs(void) {
  return OS_GetTime();
}

/*********************************************************************
*
*       COAP API
*
*/
static const IP_COAP_API _APP_Api = {
  _APP_Receive,
  _APP_Send,
  _APP_GetTimeMs
};

/*********************************************************************
*
*       _COAP_Server()
*
*  Function description
*    CoAP server application.
*/
static void _COAP_Client(void) {
  struct sockaddr_in   Address;
  U32                  Ip;
  int                  Exit;
  int                  r;

  //
  // Get the CoAP server IP address.
  //
  if (_IsIPAddress(COAP_SERVER)) {
    Ip = _ParseIPAddr(COAP_SERVER);
  } else {
    //
    // Convert host into IP address.
    //
    r = IP_ResolveHost(COAP_SERVER, &Ip, 10000);  // Resolve host name.
    if (r != 0) {
      IP_Logf_Application("Could not resolve host addr. for \"%s\"", COAP_SERVER);
      return;
    }
  }
  //
  // Initializes the connection.
  //
  _ConnInfo.hSock = (void*)socket(AF_INET, SOCK_DGRAM, 0);
  if ((int)_ConnInfo.hSock == SOCKET_ERROR) {
    IP_COAP_APP_WARN(("Socket creation error !\n"));
    return;
  }
  _ConnInfo.Family        = IP_COAP_IPV4;
  _ConnInfo.Port          = COAP_PORT;
  _ConnInfo.Addr.IPAddrV4 = Ip;
  //
  Address.sin_family      = AF_INET;
  Address.sin_port        = 0;
  Address.sin_addr.s_addr = INADDR_ANY;
  //
  if (bind((int)_ConnInfo.hSock,(struct sockaddr *)&Address, sizeof(struct sockaddr)) == -1) {
    IP_COAP_APP_WARN(("Error bind\n"));
    return;
  }
  //
  // Initializes the CoAP client.
  //
  IP_COAP_CLIENT_Init(&_COAPClient, (U8*)&_MsgBuffer[0], sizeof(_MsgBuffer), &_APP_Api);
  //
  // Configures the application.
  //
  _ConfigureClient();
  //
  // Main loop
  //
  Exit = 0;
  //
  while (Exit == 0) {
    //
    // Call the process. It will handle reception, retry, observe...
    //
    IP_COAP_CLIENT_Process(&_COAPClient);
    //
    // And call the application. CoAP client is not thread safe,
    // if a separate task is used, it is needed to protext the calls
    // to CoAP client function.
    //
    Exit = _RunApplication();
  };
  //
  // Note: If "observe" is not used, and there is only one request
  // at a time (IP_COAP_NSTART == 1), it is not needed to call
  // IP_COAP_CLIENT_Process() continuously.
  // Another way to write a client would be in this case for each
  // request:
  //   //
  //   // Get a free request.
  //   //
  //   IP_COAP_CLIENT_GetFreeRequestIdx(...)
  //   //
  //   // Initializes the request.
  //   //
  //   IP_COAP_CLIENT_SetCommand(...)
  //   IP_COAP_CLIENT_SetOptionXxx(...)
  //   ...
  //   //
  //   // Send the request.
  //   //
  //   IP_COAP_CLIENT_BuildAndSend(...)
  //   //
  //   // Do CoAP processing until the reply is received.
  //   //
  //   while (IP_COAP_CLIENT_GetLastResult(...) < 0) {
  //     IP_COAP_CLIENT_Process(...);
  //   };
  //
  //
}

/*********************************************************************
*
*       MainTask()
*
*  Function description
*    Main task executed by the RTOS to create further resources and
*    running the main application.
*/
void MainTask(void) {
  IP_Init();
  _IFaceId = IP_INFO_GetNumInterfaces() - 1;                                           // Get the last registered interface ID as this is most likely the interface we want to use in this sample.
  OS_SetPriority(OS_GetTaskID(), TASK_PRIO_IP_TASK - 1);                               // For now, this task has highest prio except IP management tasks.
  OS_CREATETASK(&_IPTCB  , "IP_Task"  , IP_Task  , TASK_PRIO_IP_TASK   , _IPStack);    // Start the IP_Task.
#if USE_RX_TASK
  OS_CREATETASK(&_IPRxTCB, "IP_RxTask", IP_RxTask, TASK_PRIO_IP_RX_TASK, _IPRxStack);  // Start the IP_RxTask, optional.
#endif
  IP_AddStateChangeHook(&_StateChangeHook, _OnStateChange);                            // Register hook to be notified on disconnects.
  IP_Connect(_IFaceId);                                                                // Connect the interface if necessary.
  while (IP_IFaceIsReadyEx(_IFaceId) == 0) {
    BSP_ToggleLED(1);
    OS_Delay(50);
  }
  BSP_ClrLED(0);
  _COAP_Client();
  //
  while (1) {
    BSP_ToggleLED(0);
    OS_Delay(500);
  };
}

/*************************** End of file ****************************/