emNet CoAP Server (Sample)
Jump to navigation
Jump to search
| IP_COAP_Server_Sample.c | |
|---|---|
| Requires modifications | No |
| Download | IP_COAP_Server_Sample.c |
This sample demonstrates how to start a CoAP server. It is pre-configured to handle two clients and five observers at once and works out-of-the-box. To test the server you can use the emNet CoAP client sample or the "coap-client" application on Linux.
Code
/*********************************************************************
* (c) SEGGER Microcontroller GmbH *
* The Embedded Experts *
* www.segger.com *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
Purpose : Sample program for embOS & emNet demonstrating a simple
CoAP server.
Additional information:
This sample comes preconfigured to handle 2 clients
and 5 observers at once and works out-of-the-box.
Preparation:
To test the server you can use the emNet CoAP client sample
or the "coap-client" application on Linux.
The following defines can be changed to configure the server:
IP_COAP_MAX_NUM_CLIENT: Maximum number of clients
IP_COAP_MAX_NUM_OBS: Maximum number of observers
Example output:
...
<using emNet CoAP client sample>
8:261 MainTask - Getting with LATE reply.
8:262 MainTask - Getting with LATE reply.
8:263 MainTask - Getting Test.
8:264 MainTask - Putting Test.
8:264 MainTask - Putting Test.
8:266 MainTask - Deleting Test.
8:267 MainTask - Putting Test.
8:268 MainTask - Putting Test.
8:268 MainTask - Getting Observer.
18:269 MainTask - Getting Observer.
28:269 MainTask - Getting Observer.
38:269 MainTask - Getting Observer.
48:269 MainTask - Getting Observer.
48:270 MainTask - Getting Observer.
...
*/
#include "RTOS.h"
#include "BSP.h"
#include "IP.h"
#include "IP_COAP.h"
#include "SEGGER.h"
#include "SEGGER_UTIL.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 NUM_SERVER_DATA 11
#define POST_DATA_LENGTH 128
#define IP_COAP_MAX_NUM_CLIENT 2 // Maximum number of clients the server can handle at the same time.
#define IP_COAP_MAX_NUM_OBS 5 // Maximum number of observers the server can handle.
/*********************************************************************
*
* 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_SERVER_CONTEXT _COAPServer;
static IP_COAP_CONN_INFO _ConnInfo;
//
// Two times the MTU. The buffer is automatically split for receive and send data.
// U32 to make sure we have a word alignment.
//
static U32 _MsgBuffer[(2 * 1500) / sizeof(U32)];
static IP_COAP_SERVER_DATA _COAPServerData[NUM_SERVER_DATA];
static IP_COAP_SERVER_CLIENT_INFO _COAPClientInfo[IP_COAP_MAX_NUM_CLIENT];
static IP_COAP_OBSERVER _COAPObservers[IP_COAP_MAX_NUM_OBS];
//
// Application's data
//
//
// Meas 1 variable for GET (piggybacked) and PUT.
//
static I16 _Meas1_Temp = 21;
//
// Average variable for GET (late reply).
//
static int _AverageTemp;
static int _AverageReady;
//
// Block transfer data. GET (piggybacked) and PUT.
//
static U8 _Data128[128 + 1] = "<-----------Block 0----------->\n<-----------Block 1----------->\n<-----------Block 2----------->\n<-----------Block 3----------->\n";
static U8 _Data128Length = 128;
//
// Block transfer data. GET (late reply).
//
static int _DelayedBlockReady;
static U8 _DelayedBlock[] = {"=======0========-------1--------_______2________########3#######::::::::4:::::::"};
//
// Observable data block. GET (piggybacked).
//
static U8 _Data64[64 + 1] = "<-----------Block 0----------->\n<-----------Block 1----------->\n";
//
// Observable sensor. GET (piggybacked) + periodic update.
//
static U16 _ObsValue = 17;
//
// POST example: 1 creation of maximum POST_DATA_LENGTH bytes.
//
static IP_COAP_SERVER_DATA _POSTServerData;
static char _POSTDataPath[64];
static int _POSTDataValid;
static U8 _POSTData[POST_DATA_LENGTH];
static U8 _POSTDataLength;
//
// Separate.
//
static int _SeparateReady;
//
// Some shortcuts for ease of maintainability.
//
static IP_COAP_SERVER_DATA* _pSDTempAverage;
static IP_COAP_SERVER_DATA* _pSDDelayedBlock;
static IP_COAP_SERVER_DATA* _pSDObsData;
static IP_COAP_SERVER_DATA* _pSDSeparate;
static U32 _RefTime;
/*********************************************************************
*
* Prototypes
*
**********************************************************************
*/
#ifdef __cplusplus
extern "C" { /* Make sure we have C-declarations in C++ programs */
#endif
void MainTask(void);
#ifdef __cplusplus
}
#endif
static U32 _APP_GetTimeMs(void);
/*********************************************************************
*
* 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
*
**********************************************************************
*/
/*********************************************************************
*
* _Temperature_Meas1_GetPayload
*
* Immediate reply (GET piggybacked) no block handling.
* Text or binary format.
*/
static int _Temperature_Meas1_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
int r;
U16 Format;
(void)pContext;
pBuffer = *ppBuffer;
Length = *pLength;
IP_COAP_APP_LOG(("Getting Temperature Measurement 1."));
//
//
// Check Accept option.
//
if (IP_COAP_GetAcceptFormat(pParam, &Format) != IP_COAP_RETURN_OK) {
//
// Accept is not defined, use plain/text as default.
//
Format = IP_COAP_CT_TXT;
}
//
if (Format == IP_COAP_CT_TXT) {
r = SEGGER_snprintf((char*)pBuffer, Length, "%d C", _Meas1_Temp);
pBuffer += r;
Length -= r;
} else if (Format == IP_COAP_CT_OCTET_STREAM) {
SEGGER_WrU16BE(pBuffer, (U16)_Meas1_Temp);
*pBuffer += 2;
Length -= 2;
} else {
IP_COAP_SERVER_SetErrorDescription(pContext, "Accept either text/plain or octect-stream");
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
//
*ppBuffer = pBuffer;
*pLength = Length;
return IP_COAP_RETURN_SEND_END;
}
/*********************************************************************
*
* _Temperature_Meas1_PutPayload
*
* Handles a PUT. Text or binary format.
*/
static int _Temperature_Meas1_PutPayload(IP_COAP_SERVER_CONTEXT* pContext, U8* pPayload, U16 Length, IP_COAP_CALLBACK_PARAM* pParam) {
int r;
int i;
U8 c;
int Sign;
U16 Format;
IP_COAP_APP_LOG(("Putting Temperature Measurement 1."));
if (Length == 0) {
return IP_COAP_RETURN_ERR;
}
if (pParam->pBlock != NULL) {
if (pParam->pBlock->Index > 0) {
IP_COAP_SERVER_SetErrorDescription(pContext, "Block transfer not supported");
return IP_COAP_RETURN_ERR;
}
}
//
// Check if there is a content format specified.
//
if (IP_COAP_GetContentFormat(pParam, &Format) != IP_COAP_RETURN_OK) {
//
// Assume plain/text.
//
Format = IP_COAP_CT_TXT;
}
//
// Format plain/text.
//
if (Format == IP_COAP_CT_TXT) {
Sign = 1;
if (*pPayload == '-') {
Sign = -1;
pPayload++;
Length--;
}
while (*pPayload == ' ') {
pPayload++;
Length--;
if (Length == 0) {
break;
}
}
r = 0;
for (i = 0; i < Length; i++) {
c = *pPayload++;
if ((c < '0') && (c > '9')) {
break;
}
r = (r * 10) + c - '0';
}
r *= Sign;
if ((r < -65536) || (r > 65535)) {
IP_COAP_SERVER_SetErrorDescription(pContext, "Takes a I16 value");
return IP_COAP_RETURN_ERR;
}
_Meas1_Temp = (I16)r;
//
// Format binary.
//
} else if (Format == IP_COAP_CT_OCTET_STREAM) {
//
// Binary data, read the first 2 bytes.
//
_Meas1_Temp = 0;
if (Length > 2) {
IP_COAP_SERVER_SetErrorDescription(pContext, "Takes a I16 value");
return IP_COAP_RETURN_ERR;
}
if (Length == 2) {
_Meas1_Temp = SEGGER_RdU16BE(pPayload);
} else {
_Meas1_Temp = *pPayload;
}
} else {
IP_COAP_SERVER_SetErrorDescription(pContext, "Accept either text/plain or octect-stream");
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
return IP_COAP_RETURN_OK;
}
/*********************************************************************
*
* _Temperature_Meas2_GetPayload
*
* Immediate reply no block handling. Octet stream.
*/
static int _Temperature_Meas2_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
U32 Temp;
(void)pContext;
IP_COAP_APP_LOG(("Getting Temperature Measurement 2."));
if (IP_COAP_CheckAcceptFormat(pParam, IP_COAP_CT_OCTET_STREAM) != IP_COAP_RETURN_OK) {
IP_COAP_SERVER_SetErrorDescription(pContext, "octet-stream");
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
pBuffer = *ppBuffer;
Length = *pLength;
//
Temp = 19;
SEGGER_WrU32BE(pBuffer, Temp);
pBuffer += 4;
Length -= 4;
//
*ppBuffer = pBuffer;
*pLength = Length;
return IP_COAP_RETURN_SEND_END;
}
/*********************************************************************
*
* _Temperature_Average_GetPayload
*
* Delayed reply no block handling. Text format.
*/
static int _Temperature_Average_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
int r;
(void)pContext;
IP_COAP_APP_LOG(("Getting Temperature Average."));
if (IP_COAP_CheckAcceptFormat(pParam, IP_COAP_CT_TXT) != IP_COAP_RETURN_OK) {
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
pBuffer = *ppBuffer;
Length = *pLength;
//
if (_AverageReady == 0) {
//
// Do the processing to get the data.
// Ask to send an ACK in the meantime.
//
_AverageTemp = (19 + _Meas1_Temp) / 2;
_AverageReady = 1;
return IP_COAP_RETURN_SEND_SEPARATE;
}
//
// Result is ready.
//
r = SEGGER_snprintf((char*)pBuffer, Length, "Average: %d", _AverageTemp);
pBuffer += r;
Length -= r;
//
// Set it back to 0 for next attempt.
//
_AverageReady = 0;
//
*ppBuffer = pBuffer;
*pLength = Length;
return IP_COAP_RETURN_SEND_END;
}
/*********************************************************************
*
* _DelayedBlock_GetPayload
*
* Late reply with block handling. Text format.
*/
static int _DelayedBlock_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
int Offset;
int Len;
int r;
IP_COAP_BLOCK_INFO* pBlock;
(void)pContext;
IP_COAP_APP_LOG(("Getting Delayed Block."));
if (IP_COAP_CheckAcceptFormat(pParam, IP_COAP_CT_TXT) != IP_COAP_RETURN_OK) {
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
//
pBuffer = *ppBuffer;
Length = *pLength;
pBlock = pParam->pBlock;
//
if (_DelayedBlockReady == 0) {
_DelayedBlockReady = 1; // Simulation of processing time.
return IP_COAP_RETURN_SEND_SEPARATE;
}
//
Offset = pBlock->Index * pBlock->Size;
Len = IP_COAP_STRLEN((char*)_DelayedBlock);
if (Len - Offset > pBlock->Size) {
Len = pBlock->Size;
r = IP_COAP_RETURN_SEND_BLOCK;
} else {
Len -= Offset;
r = IP_COAP_RETURN_SEND_END;
//
// Re-arm the delayed ack simulation on the last block.
//
_DelayedBlockReady = 0;
}
IP_COAP_MEMCPY(pBuffer, _DelayedBlock + Offset, Len);
pBuffer += Len;
Length -= Len;
//
*ppBuffer = pBuffer;
*pLength = Length;
//
return r;
}
/*********************************************************************
*
* _DataSupporting_GetPayload
*
* Immediate reply no block handling. Octet stream.
*/
static int _DataSupporting_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
U32 Temp;
(void)pContext;
IP_COAP_APP_LOG(("Getting Data Supporting."));
if (IP_COAP_CheckAcceptFormat(pParam, IP_COAP_CT_OCTET_STREAM) != IP_COAP_RETURN_OK) {
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
//
pBuffer = *ppBuffer;
Length = *pLength;
//
Temp = 77;
IP_COAP_MEMCPY(pBuffer, &Temp, 4);
pBuffer += 4;
Length -= 4;
//
*ppBuffer = pBuffer;
*pLength = Length;
return IP_COAP_RETURN_SEND_END;
}
/*********************************************************************
*
* _DataSupporting_DelHandler
*
* Indicate DELETE is authorized.
*/
static int _DataSupporting_DelHandler(IP_COAP_SERVER_CONTEXT* pContext, IP_COAP_CALLBACK_PARAM* pParam) {
(void)pContext;
(void)pParam;
//
// Always authorized.
//
return IP_COAP_RETURN_OK;
}
/*********************************************************************
*
* _Data128bytes_GetPayload
*
* Block handling of textual data.
*/
static int _Data128bytes_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
int Offset;
int Len;
int r;
(void)pContext;
IP_COAP_APP_LOG(("Getting Data."));
//
// If the content format option is present, check this is a plain/text.
//
if (IP_COAP_CheckAcceptFormat(pParam, IP_COAP_CT_TXT) != IP_COAP_RETURN_OK) {
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
//
pBuffer = *ppBuffer;
Length = *pLength;
r = IP_COAP_RETURN_SEND_BLOCK;
Len = Length;
//
if (pParam->pBlock != NULL) {
Offset = pParam->pBlock->Index * pParam->pBlock->Size;
} else {
Offset = 0;
}
//
if (Offset >= _Data128Length) {
return IP_COAP_RETURN_NO_PAYLOAD;
}
if ((Offset + Len) >= _Data128Length) {
Len = _Data128Length - Offset;
r = IP_COAP_RETURN_SEND_END;
}
IP_COAP_MEMCPY(pBuffer, _Data128 + Offset, Len);
pBuffer += Len;
Length -= Len;
//
*ppBuffer = pBuffer;
*pLength = Length;
return r;
}
/*********************************************************************
*
* _Data128bytes_PutPayload
*
* Handles a PUT. Text format.
*/
static int _Data128bytes_PutPayload(IP_COAP_SERVER_CONTEXT* pContext, U8* pPayload, U16 Length, IP_COAP_CALLBACK_PARAM* pParam) {
int Offset;
IP_COAP_APP_LOG(("Putting Data."));
//
// Request that content format is present and of type plain/text.
//
if (IP_COAP_CheckContentFormat(pParam, IP_COAP_CT_TXT, 1) != IP_COAP_RETURN_OK) {
IP_COAP_SERVER_SetErrorDescription(pContext, "Content Format plain/text requested");
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
//
if (pParam->pBlock != NULL) {
Offset = pParam->pBlock->Index * pParam->pBlock->Size;
} else {
Offset = 0;
}
//
if (Offset >= 128) {
return IP_COAP_RETURN_BUFFER_TOO_SMALL;
}
if ((Offset + Length) > 128) {
return IP_COAP_RETURN_BUFFER_TOO_SMALL;
}
IP_COAP_MEMCPY(_Data128 + Offset, pPayload, Length);
if (IP_COAP_IsLastBlock(pParam, IP_COAP_CODE_REQ_PUT) != 0) {
_Data128Length = Offset + Length;
}
return IP_COAP_RETURN_OK;
}
/*********************************************************************
*
* _POSTCreateEntry_DelHandler
*
* DELETE function for the created POST entry.
*/
static int _POSTCreateEntry_DelHandler(IP_COAP_SERVER_CONTEXT* pContext, IP_COAP_CALLBACK_PARAM* pParam) {
(void)pContext;
(void)pParam;
//
// Always authorized.
//
_POSTDataValid = 0;
_POSTDataLength = 0;
return IP_COAP_RETURN_OK;
}
/*********************************************************************
*
* _POSTCreateEntry_GetPayload
*
* GET function for the created POST entry.
*/
static int _POSTCreateEntry_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
int Offset;
int Len;
int r;
(void)pContext;
IP_COAP_APP_LOG(("Getting POST entry."));
pBuffer = *ppBuffer;
Length = *pLength;
r = IP_COAP_RETURN_SEND_BLOCK;
Len = Length;
//
if (pParam->pBlock != NULL) {
Offset = pParam->pBlock->Index * pParam->pBlock->Size;
} else {
Offset = 0;
}
//
if (Offset >= _POSTDataLength) {
return IP_COAP_RETURN_NO_PAYLOAD;
}
if ((Offset + Len) >= _POSTDataLength) {
Len = _POSTDataLength - Offset;
r = IP_COAP_RETURN_SEND_END;
}
IP_COAP_MEMCPY(pBuffer, _POSTData + Offset, Len);
pBuffer += Len;
Length -= Len;
//
*ppBuffer = pBuffer;
*pLength = Length;
return r;
}
/*********************************************************************
*
* _POSTCreateEntry_PutPayload
*
* PUT function for the created POST entry.
*/
static int _POSTCreateEntry_PutPayload(IP_COAP_SERVER_CONTEXT* pContext, U8* pPayload, U16 Length, IP_COAP_CALLBACK_PARAM* pParam) {
int Offset;
int Len;
(void)pContext;
IP_COAP_APP_LOG(("Putting POST entry."));
//
Len = Length;
if (pParam->pBlock != NULL) {
Offset = pParam->pBlock->Index * pParam->pBlock->Size;
} else {
Offset = 0;
}
//
if ((Offset >= POST_DATA_LENGTH) ||
((Offset + Len) > POST_DATA_LENGTH)) {
//
// If this is an error on the POST for creation (_POSTDataLength == 0)
// the ServerData will be removed.
//
if (_POSTDataLength == 0) {
_POSTDataValid = 0;
}
return IP_COAP_RETURN_BUFFER_TOO_SMALL;
}
//
IP_COAP_MEMCPY(_POSTData + Offset, pPayload, Len);
if (IP_COAP_IsLastBlock(pParam, IP_COAP_CODE_REQ_PUT) != 0) {
_POSTDataLength = Offset + Len;
}
return IP_COAP_RETURN_OK;
}
/*********************************************************************
*
* _POSTCreateEntry
*
* Create new entry when called with POST.
*/
static int _POSTCreateEntry(IP_COAP_SERVER_CONTEXT* pContext, IP_COAP_CALLBACK_PARAM* pParam, U32 PayloadLength, IP_COAP_SERVER_DATA** ppServerData) {
U8* pURI;
U8 Length;
//
// Only one POST data could be reated.
//
if (_POSTDataValid != 0) {
IP_COAP_SERVER_SetErrorDescription(pContext, "Maximum 1 POST");
return IP_COAP_RETURN_NOT_ALLOWED;
}
//
if (PayloadLength > POST_DATA_LENGTH) {
IP_COAP_SERVER_SetErrorDescription(pContext, "Maximum size is 128 bytes");
return IP_COAP_RETURN_BUFFER_TOO_SMALL;
}
//
if (pParam->pOptDesc->URILength >= POST_DATA_LENGTH) {
IP_COAP_SERVER_SetErrorDescription(pContext, "Maximum URI path is 64 long");
return IP_COAP_RETURN_BUFFER_TOO_SMALL;
}
if (IP_COAP_GetURIPath(pParam, &pURI, &Length) != IP_COAP_RETURN_OK) {
return IP_COAP_RETURN_ERR;
}
//
// Create the new data.
//
_POSTDataValid = 1;
IP_COAP_MEMCPY(&_POSTDataPath[0], pURI, Length);
_POSTDataPath[Length] = 0;
IP_COAP_MEMSET(&_POSTServerData, 0, sizeof(_POSTServerData));
_POSTServerData.sURI = &_POSTDataPath[0];
_POSTServerData.sDescription = "title=\"User created entry\"";
_POSTServerData.pfGETPayload = _POSTCreateEntry_GetPayload;
_POSTServerData.pfPUTPayload = _POSTCreateEntry_PutPayload;
_POSTServerData.pfDELHandler = _POSTCreateEntry_DelHandler;
//
*ppServerData = &_POSTServerData;
return IP_COAP_RETURN_OK;
}
/*********************************************************************
*
* _ObsSensor_GetPayload
*
* Immediate reply no block handling. Octect stream.
*/
static int _ObsSensor_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
(void)pContext;
IP_COAP_APP_LOG(("Getting Observer Sensor."));
if (IP_COAP_CheckAcceptFormat(pParam, IP_COAP_CT_OCTET_STREAM) != IP_COAP_RETURN_OK) {
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
pBuffer = *ppBuffer;
Length = *pLength;
//
SEGGER_WrU16BE(pBuffer, _ObsValue);
pBuffer += 2;
Length -= 2;
//
*ppBuffer = pBuffer;
*pLength = Length;
return IP_COAP_RETURN_SEND_END;
}
/*********************************************************************
*
* _ObsData_GetPayload
*
* Block handling of textual data.
*/
static int _ObsData_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
int Offset;
int Len;
int r;
(void)pContext;
IP_COAP_APP_LOG(("Getting Observer Data."));
//
// If the content format option is present, check this is a plain/text.
//
if (IP_COAP_CheckAcceptFormat(pParam, IP_COAP_CT_TXT) != IP_COAP_RETURN_OK) {
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
//
pBuffer = *ppBuffer;
Length = *pLength;
r = IP_COAP_RETURN_SEND_BLOCK;
Len = Length;
//
if (pParam->pBlock != NULL) {
Offset = pParam->pBlock->Index * pParam->pBlock->Size;
} else {
Offset = 0;
}
//
if (Offset >= 64) {
return IP_COAP_RETURN_NO_PAYLOAD;
}
if ((Offset + Len) >= 64) {
Len = 64 - Offset;
r = IP_COAP_RETURN_SEND_END;
}
IP_COAP_MEMCPY(pBuffer, _Data64 + Offset, Len);
pBuffer += Len;
Length -= Len;
//
*ppBuffer = pBuffer;
*pLength = Length;
return r;
}
/*********************************************************************
*
* _Separate_GetPayload
*
* Delayed reply no block handling. Text format.
*/
static int _Separate_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
int r;
(void)pContext;
IP_COAP_APP_LOG(("Getting with LATE reply."));
if (IP_COAP_CheckAcceptFormat(pParam, IP_COAP_CT_TXT) != IP_COAP_RETURN_OK) {
return IP_COAP_RETURN_CT_FORMAT_ERROR;
}
pBuffer = *ppBuffer;
Length = *pLength;
//
if (_SeparateReady == 0) {
//
// Ask to send an ACK in the meantime.
//
_SeparateReady = 1;
return IP_COAP_RETURN_SEND_SEPARATE;
}
//
// Result is ready.
//
r = SEGGER_snprintf((char*)pBuffer, Length, "00:00:00");
pBuffer += r;
Length -= r;
//
// Set it back to 0 for next attempt.
//
_SeparateReady = 0;
//
*ppBuffer = pBuffer;
*pLength = Length;
return IP_COAP_RETURN_SEND_END;
}
/*********************************************************************
*
* _Test_GetPayload
*
* immediate reply.
*/
static int _Test_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
int r;
(void)pContext;
(void)pParam;
IP_COAP_APP_LOG(("Getting Test."));
pBuffer = *ppBuffer;
Length = *pLength;
//
r = SEGGER_snprintf((char*)pBuffer, Length, "Test data");
pBuffer += r;
Length -= r;
//
*ppBuffer = pBuffer;
*pLength = Length;
return IP_COAP_RETURN_SEND_END;
}
/*********************************************************************
*
* _Test_PutPayload
*
* PUT function for the test entry.
*/
static int _Test_PutPayload(IP_COAP_SERVER_CONTEXT* pContext, U8* pPayload, U16 Length, IP_COAP_CALLBACK_PARAM* pParam) {
(void)pContext;
(void)pPayload;
(void)Length;
(void)pParam;
IP_COAP_APP_LOG(("Putting Test."));
return IP_COAP_RETURN_OK;
}
/*********************************************************************
*
* _Test_DelHandler
*
* DELETE function for the test entry.
*/
static int _Test_DelHandler(IP_COAP_SERVER_CONTEXT* pContext, IP_COAP_CALLBACK_PARAM* pParam) {
(void)pContext;
(void)pParam;
IP_COAP_APP_LOG(("Deleting Test."));
//
// Always authorized but don't actually delete the entry.
//
return IP_COAP_RETURN_OK_NO_DELETE;
}
/*********************************************************************
*
* _Obs_GetPayload
*
* immediate reply.
*/
static int _Obs_GetPayload(IP_COAP_SERVER_CONTEXT* pContext, U8** ppBuffer, U16* pLength, IP_COAP_CALLBACK_PARAM* pParam) {
U8* pBuffer;
U16 Length;
int r;
(void)pContext;
(void)pParam;
IP_COAP_APP_LOG(("Getting Observer."));
pBuffer = *ppBuffer;
Length = *pLength;
//
r = SEGGER_snprintf((char*)pBuffer, Length, "Obs data");
pBuffer += r;
Length -= r;
//
// Set it back to 0 for next attempt.
//
_SeparateReady = 0;
//
*ppBuffer = pBuffer;
*pLength = Length;
return IP_COAP_RETURN_SEND_END;
}
/*********************************************************************
*
* _ConfigureServer()
*
* Function description
* Configuration of the CoAP server.
*/
static void _ConfigureServer(void) {
IP_COAP_SERVER_DATA* pServerData;
//
// Add the clients and observers buffers.
//
IP_COAP_SERVER_AddClientBuffer(&_COAPServer, &_COAPClientInfo[0], IP_COAP_MAX_NUM_CLIENT);
IP_COAP_SERVER_AddObserverBuffer(&_COAPServer, &_COAPObservers[0], IP_COAP_MAX_NUM_OBS);
//
// Add a handler to perform POST command.
//
IP_COAP_SERVER_SetPOSTHandler(&_COAPServer, _POSTCreateEntry);
//
// Configure a default block size.
//
IP_COAP_SERVER_SetDefaultBlockSize(&_COAPServer, 32);
//
// Disable some features (block / observe).
//
// IP_COAP_SERVER_ConfigSet(&_COAPServer, IP_COAP_CONFIG_DISABLE_BLOCKS);
// IP_COAP_SERVER_ConfigSet(&_COAPServer, IP_COAP_CONFIG_DISABLE_OBSERVE);
//
// Configure the server data.
//
IP_COAP_MEMSET(&_COAPServerData[0], 0, sizeof(IP_COAP_SERVER_DATA) * NUM_SERVER_DATA);
//
// temperature/meas1:
// Example of a simple value supporting GET and PUT methods.
//
pServerData = &_COAPServerData[0];
pServerData->sURI = "temperature/meas1";
pServerData->sDescription = "ct=0;title=\"Textual or binary value. GET piggybacked, PUT\"";
pServerData->pfGETPayload = _Temperature_Meas1_GetPayload;
pServerData->pfPUTPayload = _Temperature_Meas1_PutPayload;
pServerData->ContentFormat = IP_COAP_CT_TXT;
IP_COAP_MEMCPY(&pServerData->ETag[0], "\x01\x02\x03\x04\x05\x06\x07\x08", 8);
pServerData->ETagLength = 8;
pServerData->MaxAge = 120;
pServerData->Size2 = 8;
pServerData->DefGetOptMask = IP_COAP_OPTMASK_ETAG | IP_COAP_OPTMASK_CONTENT_FORMAT | IP_COAP_OPTMASK_MAX_AGE | IP_COAP_OPTMASK_SIZE2;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// temperature/meas2:
// Example of a simple value supporting only GET.
//
pServerData = &_COAPServerData[1];
pServerData->sURI = "temperature/meas2";
pServerData->sDescription = "title=\"Binary value (int). GET piggybacked\"";
pServerData->pfGETPayload = _Temperature_Meas2_GetPayload;
pServerData->ContentFormat = IP_COAP_CT_OCTET_STREAM;
pServerData->DefGetOptMask = IP_COAP_OPTMASK_CONTENT_FORMAT;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// temperature/average:
// Example of a simple value with a late GET support (reply in a second message).
//
pServerData = &_COAPServerData[2];
_pSDTempAverage = pServerData;
pServerData->sURI = "temperature/average";
pServerData->sDescription = "title=\"Textual value. GET late reply.\"";
pServerData->pfGETPayload = _Temperature_Average_GetPayload;
pServerData->ContentFormat = IP_COAP_CT_TXT;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// DataSupportingDelete:
// Example of the DELETE command. As ETag is used, possible to
// test the If-Match option.
//
pServerData = &_COAPServerData[3];
pServerData->sURI = "DataSupportingDelete";
pServerData->sDescription = "title=\"Binary value (int). GET piggybacked, Support DELETE\"";
pServerData->pfGETPayload = _DataSupporting_GetPayload;
pServerData->pfDELHandler = _DataSupporting_DelHandler;
pServerData->ContentFormat = IP_COAP_CT_OCTET_STREAM;
pServerData->ETagLength = 4;
IP_COAP_MEMCPY(&pServerData->ETag[0], "\x45\x02\x21\x08", 4);
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// BlockTransfer/DelayedBlock:
// Example of a "big" data that requires block transfer.
// GET is handled with a late reply (reply in a second message).
//
pServerData = &_COAPServerData[4];
_pSDDelayedBlock = pServerData;
pServerData->sURI = "BlockTransfer/DelayedBlock";
pServerData->sDescription = "title=\"Textual value. GET late reply with block support.\"";
pServerData->pfGETPayload = _DelayedBlock_GetPayload;
pServerData->ContentFormat = IP_COAP_CT_TXT;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// BlockTransfer/Data128bytes:
// Example of a "big" data that requires block transfer.
// GET and PUT are supported.
//
pServerData = &_COAPServerData[5];
pServerData->sURI = "BlockTransfer/Data128bytes";
pServerData->sDescription = "title=\"Textual value. GET and PUT, block support. Size 128 Bytes.\"";
pServerData->pfGETPayload = _Data128bytes_GetPayload;
pServerData->pfPUTPayload = _Data128bytes_PutPayload;
pServerData->ContentFormat = IP_COAP_CT_TXT;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// ObsSensor:
// Example of an observable simple data.
// Generates a NON message at MaxAge expiry.
//
pServerData = &_COAPServerData[6];
_pSDObsData = pServerData;
pServerData->sURI = "ObsSensor";
pServerData->sDescription = "title=\"Observable value.\"";
pServerData->pfGETPayload = _ObsSensor_GetPayload;
pServerData->ObsConfig = IP_COAP_OBS_OBSERVABLE | IP_COAP_OBS_AUTO_NON_ON_MAX_AGE;
pServerData->ContentFormat = IP_COAP_CT_OCTET_STREAM;
pServerData->MaxAge = 20;
pServerData->ETagLength = 2;
IP_COAP_MEMCPY(&pServerData->ETag[0], "\x17\x18", 2);
pServerData->DefGetOptMask = IP_COAP_OPTMASK_ETAG | IP_COAP_OPTMASK_CONTENT_FORMAT | IP_COAP_OPTMASK_MAX_AGE;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// BlockTransfer/ObsData:
// Example of an observable "big" data that requires block transfer.
// Generates a CON message at MaxAge expiry.
//
pServerData = &_COAPServerData[7];
pServerData->sURI = "BlockTransfer/ObsData";
pServerData->sDescription = "title=\"Observable data block.\"";
pServerData->pfGETPayload = _ObsData_GetPayload;
pServerData->ObsConfig = IP_COAP_OBS_OBSERVABLE | IP_COAP_OBS_AUTO_CON_ON_MAX_AGE;
pServerData->ContentFormat = IP_COAP_CT_TXT;
pServerData->MaxAge = 15;
pServerData->DefGetOptMask = IP_COAP_OPTMASK_CONTENT_FORMAT | IP_COAP_OPTMASK_MAX_AGE;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// separate
// simple GET support with late reply (no piggyback).
//
pServerData = &_COAPServerData[8];
_pSDSeparate = pServerData;
pServerData->sURI = "separate";
pServerData->sDescription = "title=\"simple variable with GET with LATE reply\"";
pServerData->pfGETPayload = _Separate_GetPayload;
pServerData->ContentFormat = IP_COAP_CT_TXT;
pServerData->DefGetOptMask = IP_COAP_OPTMASK_CONTENT_FORMAT;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// test
// Used to test client: reply success to everything.
//
pServerData = &_COAPServerData[9];
pServerData->sURI = "test";
pServerData->sDescription = "title=\"test entry\"";
pServerData->pfGETPayload = _Test_GetPayload;
pServerData->pfPUTPayload = _Test_PutPayload;
pServerData->pfDELHandler = _Test_DelHandler;
pServerData->ContentFormat = IP_COAP_CT_TXT;
pServerData->ETagLength = 2;
IP_COAP_MEMCPY(&pServerData->ETag[0], "\x01\x02", 2);
pServerData->DefGetOptMask = IP_COAP_OPTMASK_ETAG | IP_COAP_OPTMASK_CONTENT_FORMAT;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
//
// obs:
// Example of a simple observable data.
// Generates a CON message at MaxAge expiry.
//
pServerData = &_COAPServerData[10];
pServerData->sURI = "obs";
pServerData->sDescription = "title=\"Simple observable data.\"";
pServerData->pfGETPayload = _Obs_GetPayload;
pServerData->ObsConfig = IP_COAP_OBS_OBSERVABLE | IP_COAP_OBS_AUTO_CON_ON_MAX_AGE;
pServerData->ContentFormat = IP_COAP_CT_TXT;
pServerData->MaxAge = 10;
pServerData->DefGetOptMask = IP_COAP_OPTMASK_CONTENT_FORMAT | IP_COAP_OPTMASK_MAX_AGE;
//
IP_COAP_SERVER_AddData(&_COAPServer, pServerData);
}
/*********************************************************************
*
* _ApplicationProcessing()
*
* Function description
* Perform periodic processing of the CoAP server application.
*/
static void _ApplicationProcessing(void) {
U32 Time;
//
// Application example: temperature/average late GET reply.
// A GET was perform and the data is now ready.
//
if (_AverageReady != 0) {
IP_COAP_SERVER_UpdateData(&_COAPServer, _pSDTempAverage, IP_COAP_TYPE_CON, 0);
}
//
// Application example: BlockTransfer/DelayedBlock late GET reply.
// A GET was perform and the data is now ready.
//
if (_DelayedBlockReady != 0) {
IP_COAP_SERVER_UpdateData(&_COAPServer, _pSDDelayedBlock, IP_COAP_TYPE_CON, 0);
}
//
// Application example: ObsSensor
// Indicate a change of its value periodically.
// Generates a new ETag.
//
Time = _APP_GetTimeMs();
if ((Time - _RefTime) > 60000) { // Warning: Rollover is not handle in this example.
_RefTime = Time;
_ObsValue++;
IP_COAP_SERVER_UpdateData(&_COAPServer, _pSDObsData, IP_COAP_TYPE_CON, 1);
}
//
// Application example: separate
// Late reply to a GET.
//
if (_SeparateReady != 0) {
IP_COAP_SERVER_UpdateData(&_COAPServer, _pSDSeparate, IP_COAP_TYPE_CON, 0);
}
}
/*********************************************************************
*
* Local functions - CoAP functionality.
*
**********************************************************************
*/
/*********************************************************************
*
* _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 ClientAddress;
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, 500);
//
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(ClientAddress);
Receive = recvfrom(SocketFD, (char*)pBuffer, BufferSize, 0,(struct sockaddr *)&ClientAddress, &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)));
}
}
}
//
// Copy the client's address to be used by the CoAP processing.
//
pInfo->Family = IP_COAP_IPV4;
pInfo->Addr.IPAddrV4 = ClientAddress.sin_addr.s_addr;
pInfo->Port = ClientAddress.sin_port;
//
// 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 = pInfo->Addr.IPAddrV4;
ClientAddress.sin_family = AF_INET;
ClientAddress.sin_port = 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_Server(void) {
struct sockaddr_in ServerAddress;
_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 = IP_COAP_DEFAULT_PORT;
//
ServerAddress.sin_family = AF_INET;
ServerAddress.sin_port = htons(_ConnInfo.Port);
ServerAddress.sin_addr.s_addr = INADDR_ANY;
//
if (bind((int)_ConnInfo.hSock,(struct sockaddr *)&ServerAddress, sizeof(struct sockaddr)) == -1) {
IP_COAP_APP_WARN(("Error binding socket\n"));
return;
}
//
// Initializes the CoAP server.
//
IP_COAP_SERVER_Init(&_COAPServer, (U8*)_MsgBuffer, sizeof(_MsgBuffer), &_APP_Api);
//
// Configuration of the server.
//
_ConfigureServer();
//
_RefTime = _APP_GetTimeMs();
//
// Main loop.
//
while (1) {
//
// Call regularly the CoAP processing function.
//
IP_COAP_SERVER_Process(&_COAPServer, &_ConnInfo);
//
// Perform the application processing.
//
_ApplicationProcessing();
}
}
/*********************************************************************
*
* 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_Server();
}
/*************************** End of file ****************************/