emNet TCP SpeedClient ChibiOS (Sample)
Jump to navigation
Jump to search
| IP_TCP_SpeedClient_ChibiOS_Sample.c | |
|---|---|
| Components required |
|
| Requires modifications | Yes |
| Download | IP_TCP_SpeedClient_ChibiOS_Sample.c |
This Sample can be used to connect to the SpeedTestServer PC application and run a speed test with ChibiOS.
For this sample, SERVER_IP_ADDR must be changed to the IP Address of the PC running the SpeedTestServer.
The SpeedTestServer PC application is part of the emNet shipment and found under Windows/IP/SpeedTestServer.
Code
/*********************************************************************
* (c) SEGGER Microcontroller GmbH *
* The Embedded Experts *
* www.segger.com *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
Purpose : Speed client for TCP/IP stack using socket interface.
Notes : For compatibility with interfaces that need to connect in
any way this sample calls connect and disconnect routines
that might not be needed in all cases.
This sample can be used for Ethernet and dial-up interfaces
and is configured to use the last registered interface as
its main interface.
*/
#include "ch.h"
#include "BSP.h"
#include "IP.h"
/*********************************************************************
*
* Local defines, configurable
*
**********************************************************************
*/
#ifndef SPEEDCLIENT_NUM_CHUNKS
#define SPEEDCLIENT_NUM_CHUNKS 1 // Number of chunks (full packets) to try to transfer at once. More could increase speed.
#endif
#define SERVER_IP_ADDR IP_BYTES2ADDR(192, 168, 88, 12) // IP address of server, for example 192.168.88.1 .
#define SERVER_PORT 1234 // Remote destination port.
#define NUMBER_OF_BYTES (4uL * 1024uL * 1024uL) // Number of bytes to transmit.
#define BUFFER_SIZE (SPEEDCLIENT_NUM_CHUNKS * (1500 - 40)) // Maximum number of bytes we can transfer at once; MTU - TCP/IP header.
#define DIRECTION 3 // 1 for receive, 2 for send, 3 for Rx & Tx .
#define USE_RX_TASK 0 // 0: Packets are read in ISR, 1: Packets are read in a task of its own.
//
// Task stack sizes that might not fit for some interfaces (multiples of sizeof(int)).
//
#ifndef APP_TASK_STACK_OVERHEAD
#define APP_TASK_STACK_OVERHEAD 0
#endif
//
// Task priorities.
//
enum {
TASK_PRIO_CLIENT = 1,
TASK_PRIO_IP_TASK // 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
};
/*********************************************************************
*
* Static data
*
**********************************************************************
*/
static IP_HOOK_ON_STATE_CHANGE _StateChangeHook;
static int _IFaceId;
static char _aRxTxBuffer[BUFFER_SIZE];
//
// Task stacks.
//
static THD_WORKING_AREA(_IPStack , TASK_STACK_SIZE_IP_TASK); // Stack of the IP_Task.
#if USE_RX_TASK
static THD_WORKING_AREA(_IPRxStack , TASK_STACK_SIZE_IP_RX_TASK); // Stack of the IP_RxTask.
#endif
static THD_WORKING_AREA(_ClientStack, 1024 + APP_TASK_STACK_OVERHEAD); // Stack of the SpeedClient task.
//
// Statistics to count all successful transmissions of NUMBER_OF_BYTES
//
static struct {
U32 RxCnt;
U32 TxCnt;
U32 ErrCnt;
} _Statistics;
/*********************************************************************
*
* Prototypes
*
**********************************************************************
*/
#ifdef __cplusplus
extern "C" { /* Make sure we have C-declarations in C++ programs */
#endif
void MainTask(void);
#ifdef __cplusplus
}
#endif
/*********************************************************************
*
* Static code
*
**********************************************************************
*/
/*********************************************************************
*
* _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.
}
}
/*********************************************************************
*
* _Receive
*
* Function description
* Sends a command to server and receives data from server.
*/
static int _Receive(long TCPSockID, unsigned Mtu) {
U8 acBuffer[20];
U8 Flag;
int NumBytesAtOnce;
U32 ReceiveCnt;
U32 ReceiveCntOverhead; // The real number of bytes received including headers (overhead is roughly calculated).
int r;
int TimeStart;
int TimeEnd;
//
// Send command and receive data
//
acBuffer[0] = 'S'; // [0:0]: Command
IP_StoreU32LE(&acBuffer[1], NUMBER_OF_BYTES); // [1:4]: Number of bytes
IP_StoreU32LE(&acBuffer[5], Mtu); // [5:8]: MTU
r = send(TCPSockID, (const char*)&acBuffer[0], 9, MSG_DONTWAIT); // Send command
if (r == SOCKET_ERROR) {
return SOCKET_ERROR;
}
ReceiveCnt = 0;
ReceiveCntOverhead = 0;
TimeStart = chVTGetSystemTimeX();
do {
NumBytesAtOnce = recv(TCPSockID, _aRxTxBuffer, sizeof(_aRxTxBuffer), 0);
if (NumBytesAtOnce <= 0) {
return SOCKET_ERROR;
} else {
ReceiveCnt += NumBytesAtOnce;
ReceiveCntOverhead += (SPEEDCLIENT_NUM_CHUNKS * 54) + NumBytesAtOnce;
}
} while (ReceiveCnt < NUMBER_OF_BYTES);
TimeEnd = chVTGetSystemTimeX();
Flag = 'X'; // Confirmation
r = send(TCPSockID, (const char*)&Flag, 1, 0);
if (r == SOCKET_ERROR) {
return SOCKET_ERROR;
}
//
// Output performance values
//
IP_Logf_Application("%lu Bytes received (without headers) in %d ms.", ReceiveCnt, (TimeEnd - TimeStart));
IP_Logf_Application("%lu Bytes received (with headers) in %d ms.", ReceiveCntOverhead, (TimeEnd - TimeStart));
IP_Logf_Application("Average transfer speed (without headers): %lu Bytes/s", (ReceiveCnt / (TimeEnd - TimeStart) * 1000));
IP_Logf_Application("Average transfer speed (with headers): %lu Bytes/s\n", (ReceiveCntOverhead / (TimeEnd - TimeStart) * 1000));
BSP_ToggleLED(1);
return 0;
}
/*********************************************************************
*
* _Send
*
* Function description
* Sends a command to server and sends data to server.
*/
static int _Send(long TCPSockID, unsigned Mtu) {
U8 acBuffer[20];
int NumBytesAtOnce;
U32 SendCnt;
U32 SendCntOverhead; // The real number of bytes sent including headers (overhead is roughly calculated).
U8 Flag;
int r;
int TimeStart;
int TimeEnd;
int SizeToSend;
//
// Send command
//
acBuffer[0] = 'R'; // [0:0]: Command
IP_StoreU32LE(&acBuffer[1], NUMBER_OF_BYTES); // [1:4]: Number of bytes
IP_StoreU32LE(&acBuffer[5], Mtu); // [5:8]: MTU
r = send(TCPSockID, (const char*) &acBuffer[0], 9, MSG_DONTWAIT); // Send command
if (r == SOCKET_ERROR) {
return SOCKET_ERROR;
}
//
// Send data
//
SendCnt = 0;
SendCntOverhead = 0;
TimeStart = chVTGetSystemTimeX();
do {
if ((NUMBER_OF_BYTES - SendCnt) < Mtu) {
SizeToSend = NUMBER_OF_BYTES - SendCnt;
} else {
SizeToSend = Mtu;
}
NumBytesAtOnce = send(TCPSockID, (const char*)&_aRxTxBuffer[0], SizeToSend, 0);
if (NumBytesAtOnce == SOCKET_ERROR) {
return NumBytesAtOnce;
} else {
SendCnt += NumBytesAtOnce;
SendCntOverhead += (SPEEDCLIENT_NUM_CHUNKS * 54) + NumBytesAtOnce;
}
} while (SendCnt < NUMBER_OF_BYTES);
TimeEnd = chVTGetSystemTimeX();
Flag = 0;
//
// Wait for response to make sure data has been sent completly
//
r = recv(TCPSockID, (char*)&Flag, 1, 0);
if (r <= 0) {
return SOCKET_ERROR;
}
//
// Output performance values
//
IP_Logf_Application("%lu Bytes sent (without headers) in %d ms.", SendCnt, (TimeEnd - TimeStart));
IP_Logf_Application("%lu Bytes sent (with headers) in %d ms.", SendCntOverhead, (TimeEnd - TimeStart));
IP_Logf_Application("Average transfer speed (without headers): %lu Bytes/s", ((SendCnt / (TimeEnd - TimeStart)) * 1000));
IP_Logf_Application("Average transfer speed (with headers): %lu Bytes/s\n", ((SendCntOverhead / (TimeEnd - TimeStart)) * 1000));
BSP_ToggleLED(1);
return 0;
}
/*********************************************************************
*
* _Client
*/
static void _Client(void* p) {
long TCPSockID;
struct sockaddr_in ServerAddr;
int ConnectStatus;
int r;
int Opt;
int Mtu;
IP_USE_PARA(p);
//
// Wait until link is up and network interface is configured.
//
while (IP_IFaceIsReadyEx(_IFaceId) == 0) {
chThdSleep(50);
}
Mtu = IP_TCP_GetMTU(_IFaceId) - 40; // MTU - TCP/IP header
while(1) {
TCPSockID = socket(AF_INET, SOCK_STREAM, 0); // Open socket
if (TCPSockID == 0) { // Error, Could not get socket
while (1) {
BSP_ToggleLED(0);
chThdSleep(20);
}
} else {
//
// Set keep alive option
//
Opt = 1;
setsockopt(TCPSockID, SOL_SOCKET, SO_KEEPALIVE, &Opt, sizeof(Opt));
//
// Connect to server
//
BSP_SetLED(0);
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(SERVER_PORT);
ServerAddr.sin_addr.s_addr = htonl(SERVER_IP_ADDR);
ConnectStatus = connect(TCPSockID, (struct sockaddr *)&ServerAddr, sizeof(struct sockaddr_in));
if (ConnectStatus != SOCKET_ERROR) {
while(1) {
if (DIRECTION & 1) {
r = _Receive(TCPSockID, Mtu);
if (r == -1) {
break;
}
_Statistics.RxCnt++;
}
if (DIRECTION & 2) {
r = _Send(TCPSockID, Mtu);
if (r == -1) {
break;
}
_Statistics.TxCnt++;
}
chThdSleep(50);
}
}
}
_Statistics.ErrCnt++;
closesocket(TCPSockID);
chThdSleep(1000);
}
}
/*********************************************************************
*
* _IP_Task()
*
* Function description
* Wrapper for void functions to ignore the given parameter.
*
* Parameters
* p: Parameter passed on thread creation.
*/
static void _IP_Task(void* p) {
IP_USE_PARA(p);
IP_Task(); // Is (by default) an endless loop by itself.
}
#if USE_RX_TASK
/*********************************************************************
*
* _IP_RxTask()
*
* Function description
* Wrapper for void functions to ignore the given parameter.
*
* Parameters
* p: Parameter passed on thread creation.
*/
static void _IP_RxTask(void* p) {
IP_USE_PARA(p);
IP_RxTask(); // Is (by default) an endless loop by itself.
}
#endif
/*********************************************************************
*
* Global functions
*
**********************************************************************
*/
/*********************************************************************
*
* 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.
chThdSetPriority(NORMALPRIO + TASK_PRIO_IP_TASK - 1); // For now, this task has highest prio except IP management tasks.
(void)chThdCreateStatic(&_IPStack[0] , sizeof(_IPStack) , NORMALPRIO + TASK_PRIO_IP_TASK , _IP_Task , NULL); // Start the IP_Task.
#if USE_RX_TASK
(void)chThdCreateStatic(&_IPRxStack[0] , sizeof(_IPRxStack) , NORMALPRIO + TASK_PRIO_IP_RX_TASK, _IP_RxTask, NULL); // Start the IP_RxTask, optional.
#endif
(void)chThdCreateStatic(&_ClientStack[0], sizeof(_ClientStack), NORMALPRIO + TASK_PRIO_CLIENT , _Client , NULL); // Start the SpeedClient task.
IP_AddStateChangeHook(&_StateChangeHook, _OnStateChange); // Register hook to be notified on disconnects.
IP_Connect(_IFaceId); // Connect the interface if necessary.
chThdSetPriority(HIGHPRIO); // Now this task has highest prio for real-time application. This is only allowed when this task does not use blocking IP API after this point.
while (1) {
chThdSleep(200);
}
}
/*************************** End of file ****************************/
