µTasker Forum

µTasker Forum => µTasker general => Topic started by: tr111 on August 28, 2007, 07:38:27 AM

Title: about the udp use!
Post by: tr111 on August 28, 2007, 07:38:27 AM
hello:
    I have download the uTaskerV1[1].3.0_M5223X_SP3!
    but i don't know how to use the udp to send and get!
     #define DEMO_UDP  but how it can work???
   
Title: Re: about the udp use!
Post by: mark on August 28, 2007, 11:22:39 AM
Hi

The UDP demo in the demo project shows the method of setting up a UDP socket on a particular port (which is necessary to receive UDP frames) and sending  data. The demo waits for a UDP frame to be received and then sends it back (echo). Although the demo doesn't generate any frames of its own, the method for sending is the same if it were to.

Here is a more complete guide to the use of UDP.

1. When no UDP socket is available
If there is no UDP socket opened on a particular port, all received UDP frames on that port are discarded. However it is possible to cause an ICMP DESTINATION UNREACHABLE to be returned (this happens irrespective of UDP support) by configuring #define ICMP_DEST_UNREACHABLE in the USE_ICMP configuration group in config.h.
Note that the ICMP destination unreachable is often disabled on PCs by default since it is considered a security risk - it confirms that there is a PC/device at the destination IP address, even if there is no UDP based service running on it.

2. Opening a UDP socket
A UDP socket must be set to listening state for UDP frames to be received.
When the UDP demo starts it creates a UDP socket and binds it as shown by the following code:

Code: [Select]
    if (!((MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS))) < 0)) {
        fnBindSocket(MyUDP_Socket, MY_UDP_PORT);                         // Bind socket
        ptrUDP_Frame    = uMalloc(sizeof(UDP_MESSAGE));                  // get some memory for UDP frame
    }
    else {
    return;                                                          // no socket - this must never happen (ensure that enough user UDP sockets have been defined - USER_UDP_SOCKETS in config.h)!!
    }

USOCKET fnGetUDP_socket(unsigned char ucTOS, int (*fnListener)(USOCKET, unsigned char, unsigned char *, unsigned short, unsigned char *, unsigned short), unsigned char ucOpts);

the first parameter TOS_MINIMISE_DELAY can in fact be ignored - it is not used.
fnUDPListner() is the UDP call back routine which is called when a UDP frame is received or other event occur relevant to the socket.
(UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS) are options which define whether the UDP checksum of transmitted frames is generated and whether the UDP check sum of received frames is checked. UDP allows frames to be sent with or without check sums.

static USOCKET MyUDP_Socket;  is the socket used for communicating

#define MY_UDP_PORT            9999                                  // Test UDP port
fnBindSocket(MyUDP_Socket, MY_UDP_PORT); This call binds the new socket to a port number.

In the demo project the port number is defined to be 9999, but it could of course be a variable value.

As the comment says, ensure that there are enough UDP sockets available in the UDP socket pool. For each socket required by the application increase the value of USER_UDP_SOCKETS in config.h.

From this point on, the UDP socket is in listening for received farmes and any activity will result in the call back being executed.

3. Receiving UDP events

Code: [Select]
// UDP data server - reception call back function
//
extern int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
switch (ucEvent) {
case UDP_EVENT_RXDATA:
//if (usPortNr != MY_UDP_PORT) break;                            // ignore false ports
//if (uMemcmp(ucIP, ucUDP_IP_Address, IPV4_LENGTH)) break;       // ignore if not from expected IP address

        //if (usLength <= UDP_BUFFER_SIZE) {                             // ignore frames which are too large
            //uMemcpy(&ptrUDP_Frame->ucUDP_Message, data, usLength);     // Send the received UDP frame back
            //fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, usLength, OWN_TASK);
        //}
        fnSendUDP(MyUDP_Socket, ucIP, usPortNr, (data - sizeof(UDP_HEADER)), usLength, OWN_TASK); // echo back from transmitting IP and port
break;

case UDP_EVENT_PORT_UNREACHABLE:                                     // we have received information that this port is not available at the destination so quit
break;
}
return 0;
}

There are in two possible events which can arrive:
UDP_EVENT_PORT_UNREACHABLE - this means that an ICMP DISTINATION UNREACHABLE frame was received which corresponds to the souce port of this UDP socket. Generally it will be returned if the user sends a UDP frame to a destination which does not (or no longer has) a listening UDP socket operating.

UDP_EVENT_RXDATA - this means that a UDP frame has been received.
 ucIP is a pointer to the IP address fo the sender
 usPortNr is the port number of the sender
 data is a pointer to the data in the UDP frame
 usLength is the length of the UDP data

As shown in the example (where the comments can be changed to suit) this allows various things to be quite simply performed:
if (usPortNr != MY_UDP_PORT) break; // this will reject UDP frames from destinations which are not using the same UDP port number as their source.

if (uMemcmp(ucIP, ucUDP_IP_Address, IPV4_LENGTH)) break; // this will reject any data from IP addresses not equal to
    static unsigned char ucUDP_IP_Address[IPV4_LENGTH] = {192, 168, 0, 102};
This is used as a form of trusted IP address.

fnSendUDP(MyUDP_Socket, ucIP, usPortNr, (data - sizeof(UDP_HEADER)), usLength, OWN_TASK); // This is the simplest form of echo message. It sends the received data back to the destination port and IP address without copying it from the receive Ethernet frame buffer. The reason for the pointer shift of sizeof(UDP_HEADER) is due to the fact that the data is passed with space for the UDP header which the UDP transmission routine uses when constructing the UDP frame. This should become clear in the next section.

4. Transmission of UDP frames

Code: [Select]
fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, usLength, OWN_TASK);
This is the general transmission method. The destintion UDP port number and destination IP address determine where the UDP frame will be sent to. MY_UDP_PORT is passed since it is also required in the UDP frame.
The data is passed in this example as a struct, where the data length is defined (usLength) and a reference to the sending task OWN_TASK is also given.

In the demo the following struct is defined for sending a UDP frame.
Code: [Select]
    typedef struct stUDP_MESSAGE
    {
        unsigned short usLength;
        UDP_HEADER     tUDP_Header;                                      // reserve header space
        unsigned char  ucUDP_Message[UDP_BUFFER_SIZE];                   // reserve message space
    } UDP_MESSAGE;
This defines the message buffer as consisting of a UDP_HEADER plus UDP data, where the buffer size UDP_BUFFER_SIZE is adequate for the application use.
When UDP data is sent, the data content is set to the buffer ucUDP_Message. When sent, the complete object is passed so that the UDP transmission routine can simply fill it out with the necessary UDP header and send it on to the IP layer. This results in improved efficiency since the UDP frame construction routine doesn't have to copy any UDP data content. It does however mean that the user must always pass the header buffer space - which is easily done when such a struct is used.

5. UDP frame retransmissions
UDP is a connectionless protocol and has also no retransmission at the UDP layer. This means that it is not a secure protocol unless a higher layer is responsible for resending lost data if it has detected a communication problem.
However there are cases where transmission is not possible and a repeat of the data must be foreseen. This is due to the ARP process and can be handled as follows - this is also built into the UDP demo code in application.c as reference.

Consider the following transmission case:

Code: [Select]
static UDP_MESSAGE *ptrUDP_Frame  = uMalloc(sizeof(UDP_MESSAGE));                  // get some memory for UDP frame
const unsigned char test_data[] = {1,2,3,4,5,6,7,8};

uMemcpy(&ptrUDP_Frame->ucUDP_Message, test_data, sizeof(test_data));     // Send the received UDP frame back
fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, sizeof(test_data), OWN_TASK);


Here a test frame is put to the UDP buffer and send using fnSendUDP().
Note that the UDP frame buffer has been created on the heap and maintains a backup of this frame. This is important if the data is to be retransmitted at a later point in time.
Although the return value of fnSendUDP() is not being checked, it can have the following values:
0 means that no data could be transmitted due to a problem with the Ethernet interface. In this case the data has been lost - it is an exceptional case which is generally not expected.
> 0 means that this amount of data was transmitted (including IP and UDP headers). It means that the transmission to the destination address via the Ethernet interface was successful but it doesn't mean that the UDP frame will ever arrive. However, if there is no higher level protocol being executed the job has in fact been completed.

INVALID_SOCKET_HANDLE will be returned if the socket is invalid or
SOCKET_CLOSED if the socket is not bound.
INVALID_DEST_IP will be returned if the user tries to send to the IP address 0.0.0.0.
INVALID_REMOTE_PORT if the user tries to send to the destination UDP port number 0 or
ZERO_PORT if the user source port is declared as 0.

NO_ARP_ENTRY will be returned if the IP frame (consisting of IP and UDP header plus UDP data) could not be sent due to a missing MAC <-> IP entry in the ARP table. This is not an error but a natural occurance in an Ethernet LAN, which starts the ARP resolve process. ARP sends a broadcast request to attempt to resolve the destination IP address in the local network, which can be either successful or fail. Since this ARP resolve process can take some time the result is sent to the sending task (remember the parameter OWN_TASK) as soon as it is known. In the case of the fail it can take several seconds until ARP actually gives up and declares the process as a failed attempt.

The demo project example shows how the ARP result messages are processed:

Code: [Select]
        case  TASK_ARP:
            fnRead( PortIDInternal, ucInputMessage, ucInputMessage[MSG_CONTENT_LENGTH]); // read the contents
            switch (ucInputMessage[ 0 ]) {                               // ARP sends us either ARP resolution success or failed
            case ARP_RESOLUTION_SUCCESS:                                 // IP address has been resolved (repeat UDP frame).
                fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
                break;

            case ARP_RESOLUTION_FAILED:                                  // IP address could not be resolved...
                break;
            }
            break;

This is in the task's input queue read, where the ARP task is identified as the message source.
The message type ARP_RESOLUTION_FAILED informs that the destination IP address is not reachable on the network. In this demo example nothing is undertaken but the message could be of interest in an application to generate a user message informing of the reason for the failure.

The ARP_RESOLUTION_SUCCESS message informs that the destination IP address has now been resolved and a retransmission of the original message should now be performed. This retransmission is not necessarily an application layer retranmission but is rather necessary to be performed due to the ARP process which can and will take place in practice. This means that its handling MUST be resolved if UDP transmission is to operate correctly.
As can be seen the backed up UDP frame is simply resent, which will in this case almost certainly be successful.

Note that in an application where a lot of UDP data is to be transmitted it is probably best to not allow further transmissions to be attempted until the ARP resolution process has completed. This will avoid having to back up lots of UDP data frames. If several tasks are sending UDP frames, each task can use the same mechanism but will have to back up its own message in each case.

If you have studied the TCP implementation you will see that the handling of the ARP RESOLUTION is resolved by the TCP_EVENT_REGENERATE event in the TCP call back routine, This call back routine has however been dispatched from the same ARP message reception in the TCP task.


That is about all that is needed to be known to be able to successfully use UDP in the project.

Good luck!!

Regards

Mark





Title: Re: about the udp use!
Post by: tr111 on August 29, 2007, 01:30:24 AM
Thank you very much ! mark !
Title: Re: about the udp use!
Post by: Tino on January 23, 2008, 05:07:00 PM
Hi,
Now I have a problem with UDP:

static USOCKET UDP_data_socket = -1;

static int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength);

void StartListen(void) {
   if (((UDP_data_socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS))) >= 0)) {
        fnBindSocket(UDP_data_socket, RECEIVE_DATA_PORT_NUMBER);                         // Bind socket
    }
    else {
       return;
    }
}

static int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength) {
...
}


When I comment
fnBindSocket(UDP_data_socket, RECEIVE_DATA_PORT_NUMBER);                         // Bind socket
it works.
But if it is uncommented, the mcu crashes and resets.
Strange things is, that this worked before.
I have enough udp sockets reserved (10).

Help :)
Tino
Title: Re: about the udp use!
Post by: mark on January 23, 2008, 05:47:00 PM
Tino

Do you know where the code is crashing? - I don't see that fnBindSocket() itself can be the cause of the crash. This routine only fills out variables when the socket and its internal pointers are valid.

Is it possible that the crash occurs when data has been received? In this case the problem may be in the listener call-back routine. If the socket if not bound it will not be listening and so the call back will not be executed.

Regards

Mark
Title: Re: about the udp use!
Post by: Tino on January 23, 2008, 07:54:30 PM
Someday I will go nuts...
Now it works again.
I will try to make it not work again and then debug.

Thanks for your reply,
Tino
Title: Re: about the udp use!
Post by: neil on March 14, 2008, 12:38:40 PM
Hi,
  I am new to UDP, and have always used TCP. I understand (well, think), that UDP is connectionless, but that is as far as it goes.
The application I need is not being a server, but connecting to a server device. I simply want to connect to a server, send/receve data and close (well, dont know if there is a close command in UDP).

Thanks
Neil
Title: Re: about the udp use!
Post by: mark on March 14, 2008, 01:07:42 PM
Hi Neil

Since UDP is connectionless there is no way to connect to a server - you can simply send data to the server whenever you want - either it arrives (no ack) or it doesn't (also no ack). If the server is not actually running but the PC it is on is, it may be possible that you receive an ICMP DESTINATION UNREACHABLE, but this is also not guarantied since this is often disabled at the PC (considered as a security weakness in some situations when activated).

If the application needs more reliability you can add a protocol layer above the UDP layer which then performs its own protocol (ACK,NACK etc.). If reliability is important the application must take over this responsibility - or change to TCP/IP as protocol where it is handled.

Regards

Mark
Title: Re: about the udp use!
Post by: neil on April 03, 2008, 12:44:51 PM
Hi Mark,
  I have an application where I will be communicating with 1 piece of hardware using UDP( It is local to the board, about 3 feet away). My application will not be a server but a client. I understand that a UDP cannot make a connection, but their documentation regarding the UDP does mention a connection first. I looked into my trusty book 'Network programming for microsoft windows', and it does mention a connection (even though it does nothing) , here is the details from the book:

'Another method of receiving and sending data on a connectionless socket is to establish a connection. This might seem strange, but its not quite what it sounds like. Once the socket is created, you can call connect. No actual connection is made, however. The socket address passed into a connect function is associated with the socket so recv can be used  instead of recvfrom because the datas origin is known. The capability to connect a datagram socket is handy if you intend to communicate with only one endpoint at a time.'

My application will only be communicating (well with one socket, might have a tcp socket on another connection to my application on a windows machine)with one endpoint. Is this the way to go for my UDP socket?

Thanks
Neil
Title: Re: about the udp use!
Post by: mark on April 03, 2008, 04:20:24 PM
Hi Neil

I am not sure exactly what this connect does but it sounds like something conceptional rather than to acually do with the protocol. It may be sort of entering the port number for the UDP connection (does 'endpoint' mean a IP / Port pair?) so that it doesn't have to be passed each time it is read.

From the embedded side it probably doesn't have any real significance.

Regards

Mark
Title: Re: about the udp use!
Post by: thamanjd on April 04, 2008, 06:07:40 AM
May we ask what language/compiler you were going to use for the PC software?
Title: Re: about the udp use!
Post by: thamanjd on May 06, 2008, 03:25:10 AM
can you clarify for me what fnReleaseUDP_socket does please?
What actually gets freed - it looks like the socket handle is left alone?

Title: Re: about the udp use!
Post by: mark on May 06, 2008, 02:15:28 PM
Hi

Rather than answering this here I have added the complete function description to the (new) on-line documentation.

To view this please go to: http://www.utasker.com/docs/Code.html

As you will see there are only a few functions which are presently documented and the layout needs some extra work to get it operating with all Browsers (Firefox works quite well when the screen is not too narrow). However I have just added the fnGetUDP_socket() and fnReleaseUDP_socket() descriptions which will hopefully clarify everthing!

Regards

Mark
Title: Re: about the udp use!
Post by: Marco on February 10, 2009, 01:48:43 PM
Hi Mark

I have tried all day and gotten no where, this is the code I got so far.

#include "config.h"


extern void fnMyFirstTask(TTASKTABLE *ptrTaskTable)
{

  fnBindSocket(2, 1234);
  fnSendUDP(2, (192, 168, 0, 101) ,1234, (unsigned char*)"Hello World", 11, OWN_TASK);

}

The error I'm getting is that OWN_TASK is not defined, you wouldn't happen to have an example of sending a hello world from a new app?

cheers

Marco
Title: Re: about the udp use!
Post by: mark on February 10, 2009, 05:48:06 PM
Hi Marco

If you activate DEMO_UDP in application.c it will add a demo. There is a listener which echoes back received frames.

OWN_TASK is the name of the owner task which is informed of transmission errors and ARP resolution if the MAC address of the destination has first to be resolved. See the case ARP_RESOLUTION_SUCCESS in application.c where the UDP frame is repeated on ARP resolution success.

Eg. in application.c it is
#define OWN_TASK                  TASK_APPLICATION

In your own task you can just locally define
#define OWN_TASK                  MY_TASK         , for example.


  fnBindSocket(2, 1234);

Before you can bind a socket you need to get one from the UDP pool. Eg.
MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS));

Then you can bind it - you need to pass the socket to be bound and not a fixed number.
  fnBindSocket(MyUDP_Socket, 1234);

Make sure that there are enough UDP sockets in the pool by incrementing USER_UDP_SOCKETS (in config.h) for each extra one your application needs.


  fnSendUDP(2, (192, 168, 0, 101) ,1234, (unsigned char*)"Hello World", 11, OWN_TASK);

Again you need to pass the socket and not a fixed number.

I am not sure about the (192, 168, 0, 101). I think that this is passing an array of ints - it should be an array of unsigned chars.
It is best to do
const unsigned char ucIP[] = {192,168,0,101}; and pass this.

Finally there is a mistake with passing the string. Here you need to pass a UDP struct which has space for the UDP header to be added to it. You are passing a const pointer and this will probably cause the code to crash when it tries to add the header (it would also overwrite some data).


You need to build and send your message (compare with the demo code), for example like this:

            uMemcpy(&ptrUDP_Frame->ucUDP_Message, "Hello World", 11);
            fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, 1234, (unsigned char*)&ptrUDP_Frame->tUDP_Header, 11, OWN_TASK);


Regards

Mark

Title: Re: about the udp use!
Post by: Marco on February 11, 2009, 12:45:08 PM
Thanks a bunch!  I think i understand now whats going on there.  I'm going to try that right now!

Cheers

Marco
Title: Re: about the udp use!
Post by: Marco on February 11, 2009, 05:12:00 PM
Hi Mark

Sorry, I'm stuck again.  Ok this is what I got so far, how ever the compiler sais fnUDPListner is not declared.

Code: [Select]
#include "config.h"


extern void fnFBWOE(TTASKTABLE *ptrTaskTable)
{
  #define OWN_TASK    fnFBWOE
  static USOCKET MyUDP_Socket;
 
  MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS);
  fnBindSocket(MyUDP_Socket, 1234);
 
  const unsigned char ucIP[] = {192,168, 0, 101};
 
  uMemcpy(&ptrUDP_Frame->ucUDP_Message, "Hello World", 11);
  fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, 1234, (unsigned char*)&ptrUDP_Frame->tUDP_Header, 11, OWN_TASK);
}

I tried to declare it as the following, but the compiler sais its an ileagal storage class.

Code: [Select]
  static int  fnUDPListner(USOCKET c, unsigned char uc, unsigned char *ucIP, unsigned short us, unsigned char *data, unsigned short us2);

What should I declare fnUDPListner as?


Cheers

Marco
Title: Re: about the udp use!
Post by: mark on February 11, 2009, 06:58:40 PM
Hi Marco

The listener is required for receiving UDP data on the defined UDP port.


// UDP data server - reception call back function
//
static int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
   return 0;
}


You must supply the listener. If you don't actually want to receive any data on the port you can just add a dummy one like the one above. Put it before your code and you won't need to prototype it. Otherwise add:

static int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength);

Regards

Mark

Title: Re: about the udp use!
Post by: Marco on February 14, 2009, 12:53:26 PM
Hi Mark

Thanks I got that part to work, and I aslo figured out how to define MyUDP_Socket, I now have problems with ptrUDP_Frame.  Can you please tell me how to define ptrUDP_Frame.  I searched for it, but could not find it.

Code: [Select]
#include "config.h"


static int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
  return 0;
}

extern void fnFBWOE(TTASKTABLE *ptrTaskTable)
{
  #define OWN_TASK    fnFBWOE
  #define UDP_BUFFER_SIZE        11                                   // Buffer size for UDP test message
  #define MY_UDP_PORT            1234
  static USOCKET MyUDP_Socket;
  UDP_HEADER     tUDP_Header;
 
  unsigned char  ucUDP_Message[UDP_BUFFER_SIZE];
  const unsigned char ucIP[IPV4_LENGTH]= {192, 168, 0, 101};
   
  MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS));
  fnBindSocket(MyUDP_Socket, MY_UDP_PORT);
 
  uMemcpy(&ptrUDP_Frame->ucUDP_Message, "Hello World", UDP_BUFFER_SIZE);
  fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
}

Cheers

Marco
Title: Re: about the udp use!
Post by: mark on February 14, 2009, 03:40:51 PM
Hi Marco

static UDP_MESSAGE *ptrUDP_Frame;
ptrUDP_Frame    = uMalloc(sizeof(UDP_MESSAGE));                  // get some memory for UDP frame


Regards

Mark
Title: Re: about the udp use!
Post by: Marco on February 15, 2009, 06:05:57 AM
Hi Mark

I have actually tried "static UDP_MESSAGE *ptrUDP_Frame;" im getting an error "C2540: Expected: ,".  I'm not sure why that is.

Code: [Select]
#include "config.h"


static int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
  return 0;
}

extern void fnFBWOE(TTASKTABLE *ptrTaskTable)
{
  #define OWN_TASK    fnFBWOE
  #define UDP_BUFFER_SIZE        11                                   // Buffer size for UDP test message
  #define MY_UDP_PORT            1234
  static USOCKET MyUDP_Socket;
  UDP_HEADER     tUDP_Header;
  static UDP_MESSAGE *ptrUDP_Frame;
  ptrUDP_Frame    = uMalloc(sizeof(UDP_MESSAGE));                  // get some memory for UDP frame
 
  unsigned char  ucUDP_Message[UDP_BUFFER_SIZE];
  const unsigned char ucIP[IPV4_LENGTH]= {192, 168, 0, 101};
   
  MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS));
  fnBindSocket(MyUDP_Socket, MY_UDP_PORT);
 
  uMemcpy(&ptrUDP_Frame->ucUDP_Message, "Hello World", UDP_BUFFER_SIZE);
  fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
}

Cheers

Marco
Title: Re: about the udp use!
Post by: mark on February 15, 2009, 11:54:09 AM
Hi Marco

The following will work:


extern void fnFBWOE(void)
{
  #define OWN_TASK    fnFBWOE
  #define UDP_BUFFER_SIZE        11                                   // Buffer size for UDP test message
  #define MY_UDP_PORT            1234
  static USOCKET MyUDP_Socket;
  static UDP_MESSAGE *ptrUDP_Frame;
  const unsigned char ucIP[IPV4_LENGTH]= {192, 168, 0, 101};
  ptrUDP_Frame    = uMalloc(sizeof(UDP_MESSAGE));                  // get some memory for UDP frame   
  MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS));
  fnBindSocket(MyUDP_Socket, MY_UDP_PORT);
 
  uMemcpy(&ptrUDP_Frame->ucUDP_Message, "Hello World", UDP_BUFFER_SIZE);
  fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
}


1) UDP_HEADER     tUDP_Header; and unsigned char  ucUDP_Message[UDP_BUFFER_SIZE]; are unused so I removed them
2) Your error was the uMalloc() position - it was before the static variable end - which is illegal syntax
3) What you still need to add is the handling of the ARP_RESOLUTION_SUCCESS event.

The first time your routine sends the IP address is probably not in the ARP table. This causes ARP to send a request to resolve this on the local network. Your task will be informed when this has been completed and then it needs to repeat the message. When further UDP frames are sent the IP address will have been resolved and so will be immediately sent (until the IP entry times out in the ARP table and the process will then be repeated)


In your task you thus need

    unsigned char       ucInputMessage[HEADER_LENGTH];                  // reserve space for receiving messages
....

    while ( fnRead( PortIDInternal, ucInputMessage, HEADER_LENGTH )) {   // check input queue
        switch ( ucInputMessage[MSG_SOURCE_TASK] ) {                     // switch depending on message source
        case  TASK_ARP:
            fnRead( PortIDInternal, ucInputMessage, ucInputMessage[MSG_CONTENT_LENGTH]); // read the contents
            switch (ucInputMessage[ 0 ]) {                               // ARP sends us either ARP resolution success or failed
            case ARP_RESOLUTION_SUCCESS:                                 // IP address has been resolved (repeat UDP frame).
                fnSendUDP(MyUDP_Socket, ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
                break;

            case ARP_RESOLUTION_FAILED:                                  // IP address could not be resolved...
                break;
            }
            break;
        }
    }



Regards

Mark
Title: Re: about the udp use!
Post by: Marco on February 15, 2009, 06:27:45 PM
Hi Mark

I'm now getting loads of compile errors, it still has a problem with static UDP_MESSAGE *ptrUDP_Frame;.  The error is "2450: Expected: ,"

By the way am I suposed to change extern void fnFBWOE(TTASKTABLE *ptrTaskTable)  to extern void fnFBWOE(void)  as that also gives me compile errors.

Code: [Select]
#include "config.h"


static int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
  return 0;
}

extern void fnFBWOE(TTASKTABLE *ptrTaskTable) 
{
  #define OWN_TASK    fnFBWOE
  #define UDP_BUFFER_SIZE        11                                   // Buffer size for UDP test message
  #define MY_UDP_PORT            1234
  unsigned char       ucInputMessage[HEADER_LENGTH];                  // reserve space for receiving messages
  static USOCKET MyUDP_Socket;
  static UDP_MESSAGE *ptrUDP_Frame;
  const unsigned char ucIP[IPV4_LENGTH]= {192, 168, 0, 101};
  ptrUDP_Frame    = uMalloc(sizeof(UDP_MESSAGE));                  // get some memory for UDP frame   
  MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS));
  fnBindSocket(MyUDP_Socket, MY_UDP_PORT);
 
  uMemcpy(&ptrUDP_Frame->ucUDP_Message, "Hello World", UDP_BUFFER_SIZE);
  fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
 
  while ( fnRead( PortIDInternal, ucInputMessage, HEADER_LENGTH )) {   // check input queue
      switch ( ucInputMessage[MSG_SOURCE_TASK] ) {                     // switch depending on message source
      case  TASK_ARP:
          fnRead( PortIDInternal, ucInputMessage, ucInputMessage[MSG_CONTENT_LENGTH]); // read the contents
          switch (ucInputMessage[ 0 ]) {                               // ARP sends us either ARP resolution success or failed
          case ARP_RESOLUTION_SUCCESS:                                 // IP address has been resolved (repeat UDP frame).
              fnSendUDP(MyUDP_Socket, ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
              break;

          case ARP_RESOLUTION_FAILED:                                  // IP address could not be resolved...
              break;
          }
          break;
      }
  }


}

Cheers

Marco
Title: Re: about the udp use!
Post by: mark on February 15, 2009, 07:00:00 PM
Hi Marco

There are still missing defines and (I only just noticed) the OWN_TASK definition is incorrect.

I have written the task for you below. This will send the test message and then repeat it very 10s (see the timer).
I set OWN_TASK to MY_TASK - if you have a different name you can set it correctly.

Also make sure that the task is started after the Ethernet task has started. Trying to send frames before the Ethernet interface has been initialized will lead to errors.  To ensure correct start up it is usually easiest to start the task from application.c using

uTaskerStateChange(MY_TASK, UTASKER_ACTIVATE);



Code: [Select]
#include "config.h"

#define OWN_TASK               MY_TASK

#define UDP_BUFFER_SIZE        11                                        // buffer size for UDP test message
#define MY_UDP_PORT            1234

#define E_TIMER_NEXT_MESSAGE   1

typedef struct stUDP_MESSAGE
{
    unsigned short usLength;
    UDP_HEADER     tUDP_Header;                                          // reserve header space
    unsigned char  ucUDP_Message[UDP_BUFFER_SIZE];                       // reserve message space
} UDP_MESSAGE;

static int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
    return 0;
}

extern void fnFBWOE(TTASKTABLE *ptrTaskTable) 
{
    static USOCKET MyUDP_Socket = -1;
    static UDP_MESSAGE *ptrUDP_Frame;
    const unsigned char ucIP[IPV4_LENGTH]= {192, 168, 0, 101};
    QUEUE_HANDLE        PortIDInternal = ptrTaskTable->TaskID;           // queue ID for task input
    unsigned char       ucInputMessage[HEADER_LENGTH];                   // reserve space for receiving messages

    if (MyUDP_Socket < 0) {                                              // only perform once
        ptrUDP_Frame    = uMalloc(sizeof(UDP_MESSAGE));                  // get some memory for UDP frame   
        MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS));
        fnBindSocket(MyUDP_Socket, MY_UDP_PORT);
        uMemcpy(&ptrUDP_Frame->ucUDP_Message, "Hello World", UDP_BUFFER_SIZE);
        fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
        uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(10*SEC), E_TIMER_NEXT_MESSAGE );
    }

    while ( fnRead( PortIDInternal, ucInputMessage, HEADER_LENGTH )) {   // check input queue
        switch ( ucInputMessage[MSG_SOURCE_TASK] ) {                     // switch depending on message source
        case TIMER_EVENT:
            if (E_TIMER_NEXT_MESSAGE == ucInputMessage[MSG_TIMER_EVENT]) {
                fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
                uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(10*SEC), E_TIMER_NEXT_MESSAGE );
            }
            break;

        case  TASK_ARP:
            fnRead( PortIDInternal, ucInputMessage, ucInputMessage[MSG_CONTENT_LENGTH]); // read the contents
            switch (ucInputMessage[ 0 ]) {                               // ARP sends us either ARP resolution success or failed
                case ARP_RESOLUTION_SUCCESS:                             // IP address has been resolved (repeat UDP frame).
                    fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
                    break;

                case ARP_RESOLUTION_FAILED:                              // IP address could not be resolved...
                    break;
            }
            break;
        }
    }
}

A task must always have the parameter (TTASKTABLE *ptrTaskTable)


Regards

Mark


Title: Re: about the udp use!
Post by: Marco on February 16, 2009, 07:58:49 AM
Hi Mark

Thanks for that I realy apprechiate the help! Am i suposed to remove the following from TaskConfig.h?

Code: [Select]
  { "fnFBWOE", fnFBWOE, NO_QUE, (DELAY_LIMIT)(5 * SEC), (DELAY_LIMIT)(2 * SEC), UTASKER_STOP},  // fnFBWOE Task run after 5s and run periodically every 2 seconds.

Do I have to change anything else in TaskConfig.h?

I put "uTaskerStateChange(fnFBWOE, UTASKER_ACTIVATE);" just after "uTaskerStateChange(TASK_DEBUG, UTASKER_ACTIVATE);" in application.c.  I'm getting an error "expected signed char, given void".  Is there anything else I'm suposed to do in application.c?

Cheers

Marco
Title: Re: about the udp use!
Post by: mark on February 16, 2009, 12:05:02 PM
Hi Marco

uTaskerStateChange(fnFBWOE, UTASKER_ACTIVATE);

This is not correct, you need to use

uTaskerStateChange(MY_TASK, UTASKER_ACTIVATE);

where MY_TASK is the task reference that you have given in TaskConfig.h, eg.

#define TASK_MODBUS             'f'

Make sure that you have also added this to the task list ctNodes[].

I see that you have configured the task to be delayed and then started periodically. You can change this to

  { "fnFBWOE",      fnFBWOE,         SMALL_QUEUE, (DELAY_LIMIT)(NO_DELAY_RESERVE_MONO), 0, UTASKER_STOP},

in order to use the monostable task timer in the last code, the task must have a queue as well.

Regards

Mark

Title: Re: about the udp use!
Post by: Marco on February 16, 2009, 03:51:46 PM
Thank you so much!  It works, I realy apprechiate all the help!

Thanks again

Marco
Title: Re: about the udp use!
Post by: Marco on February 24, 2009, 02:21:53 PM
Hi Mark, I continued to play around with UDP, I have now grouped all the ADC values along with some other variables into a char array and I'm sending out an updated packet every 50ms.

Code: [Select]
static char StrMsg[36]= "String Build - Error"; // If string is not built this messege will be sent.

#define OWN_TASK               TASK_SUDP

#define UDP_BUFFER_SIZE        37                                        // buffer size for UDP test message
#define MY_UDP_PORT            1234

#define E_TIMER_NEXT_MESSAGE   1

typedef struct stUDP_MESSAGE
{
    unsigned short usLength;
    UDP_HEADER     tUDP_Header;                                          // reserve header space
    unsigned char  ucUDP_Message[UDP_BUFFER_SIZE];                       // reserve message space
} UDP_MESSAGE;

static int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
    return 0;
}


// <<<<<<<<<<<<<<<<<<< Send UDP Function >>>>>>>>>>>>>>>>
extern void fnSUDP(TTASKTABLE *ptrTaskTable) 
{
    static USOCKET MyUDP_Socket = -1;
    static UDP_MESSAGE *ptrUDP_Frame;
    const unsigned char ucIP[IPV4_LENGTH]= {192, 168, 0, 101};
    QUEUE_HANDLE        PortIDInternal = ptrTaskTable->TaskID;           // queue ID for task input
    unsigned char       ucInputMessage[HEADER_LENGTH];                   // reserve space for receiving messages
   
    if (MyUDP_Socket < 0) {                                              // only perform once
        ptrUDP_Frame    = uMalloc(sizeof(UDP_MESSAGE));                  // get some memory for UDP frame   
        MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS));
        fnBindSocket(MyUDP_Socket, MY_UDP_PORT);
        uMemcpy(&ptrUDP_Frame->ucUDP_Message,StrMsg, UDP_BUFFER_SIZE);
        fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
        uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(0.05*SEC), E_TIMER_NEXT_MESSAGE );
    }

    while ( fnRead( PortIDInternal, ucInputMessage, HEADER_LENGTH )) {   // check input queue
        switch ( ucInputMessage[MSG_SOURCE_TASK] ) {                     // switch depending on message source
        case TIMER_EVENT:
            if (E_TIMER_NEXT_MESSAGE == ucInputMessage[MSG_TIMER_EVENT]) {
                uMemcpy(&ptrUDP_Frame->ucUDP_Message,StrMsg, UDP_BUFFER_SIZE);
                fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
                uTaskerMonoTimer( OWN_TASK, (DELAY_LIMIT)(0.05*SEC), E_TIMER_NEXT_MESSAGE );
            }
            break;

        case  TASK_ARP:
            fnRead( PortIDInternal, ucInputMessage, ucInputMessage[MSG_CONTENT_LENGTH]); // read the contents
            switch (ucInputMessage[ 0 ]) {                               // ARP sends us either ARP resolution success or failed
                case ARP_RESOLUTION_SUCCESS:                             // IP address has been resolved (repeat UDP frame).
                    uMemcpy(&ptrUDP_Frame->ucUDP_Message,StrMsg, UDP_BUFFER_SIZE);
                    fnSendUDP(MyUDP_Socket, (unsigned char *)ucIP, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
                    break;

                case ARP_RESOLUTION_FAILED:                              // IP address could not be resolved...
                    break;
            }
            break;
        }
    }
}

I wrote a simple program, to convert an unsigned intiger into 2 chars, I'm sure there is a simpler way to do it in C.

Code: [Select]
// <<<<<<<<<<<<<  Convert 2byte int to 2 chars >>>>>>>>>>>>>>>>>>
unsigned char IntToString(unsigned int InpInt, int ChrNum)
{
  char OutChar[3];
  char RetChar;
  OutChar[1]=(char)(InpInt / 256);
  OutChar[2] =(char)(InpInt%256);
  RetChar = OutChar[ChrNum];
  return((char)RetChar);
}

I then wrote a program which is on a timer from TaskConfig.h, which then combines all the chars into StrMsg.

Code: [Select]
extern void fnBuildPacket(TTASKTABLE *ptrTaskTable) 
{
    char StrStart[2]= "[";  //Start of Data
    char StrStop[2]= "]";   //Stop of Data
    unsigned int intTPS;
    unsigned int intSPS;
    unsigned int intSpd;
    unsigned int intRpm;
    unsigned int intTmp;
    unsigned int intPsi;
    unsigned int intAlt;
    unsigned int intOLS;
    unsigned int intFLS;
    unsigned int intTrm;
    char chrRSK = 'R';
    char chrFNR = 'N';
    char chrCrC = 'F';
    char chrSpL = 'F';
    char chrSyC = 'T';
     
    static int cntrA;  // Counter needed for endcap placement on Packet
    static int cntrB;  // Counter needed for 2byte data segment placement on packet
   
    static unsigned int cntrC = 0;

    cntrC++;   //Test Data used to check update of transmission.
   
    intTPS = ATDDR0H;  // ADC channel 0
    intSPS = ATDDR1H;  // ADC channel 1
    intSpd = ATDDR2H;  // ADC channel 2
    intRpm = ATDDR3H;  // ADC channel 3
    intTmp = ATDDR4H;  // ADC channel 4
    intPsi = ATDDR5H;    // ADC channel 5
    intAlt = ATDDR6H;    // ADC channel 6
    intOLS = ATDDR7H;  // ADC channel 7
    intFLS = cntrC;
    intTrm = cntrC;
   
// <<<<<<<<<<<<<<<<< MSG string Construction >>>>>>>>>>>>>>   
   
    // Place the endcaps on the string
    for (cntrA = 0; cntrA <= 1; cntrA++)
    {
      StrMsg[cntrA] = StrStart[cntrA]; //Add the start Bracket
      StrMsg[cntrA + 26] = StrStop[cntrA]; //Add the Stop Bracket

    }
    // Add all the 2 Byte data segments
    for (cntrB = 0; cntrB <= 1; cntrB++)
    {
      StrMsg[cntrB + 1] = IntToString(intTPS, cntrB + 1);
      StrMsg[cntrB + 3] = IntToString(intSPS, cntrB + 1);
      StrMsg[cntrB + 5] = IntToString(intSpd, cntrB + 1);
      StrMsg[cntrB + 7] = IntToString(intRpm, cntrB + 1);
      StrMsg[cntrB + 9] = IntToString(intTmp, cntrB + 1);
      StrMsg[cntrB + 11] = IntToString(intPsi, cntrB + 1);
      StrMsg[cntrB + 13] = IntToString(intAlt, cntrB + 1);
      StrMsg[cntrB + 15] = IntToString(intOLS, cntrB + 1);
      StrMsg[cntrB + 17] = IntToString(intFLS, cntrB + 1);
      StrMsg[cntrB + 19] = IntToString(intTrm, cntrB + 1);
    }
    // Add aditional Controlls
    StrMsg[21] = chrRSK;
    StrMsg[22] = chrFNR;
    StrMsg[23] = chrCrC;
    StrMsg[24] = chrSpL;
    StrMsg[25] = chrSyC;
}

I just thought its a nice thing to play around with and mabe someone might find it usefull, I also uploaded my UDP test program for windows. Which makes use of winsock32
Title: Re: about the udp use!
Post by: mark on February 24, 2009, 05:19:01 PM
Hi Marco

Many thanks for that contribution.

Also take a look at the function
extern CHAR          *fnBufferHex(unsigned long ulValue, unsigned char uLen, CHAR *pBuf);           // take a value and convert it to a string in a buffer

You may also be able to use that instead of the integer to string conversion. For conversion to decimal there is the function
extern CHAR *fnDebugDec(signed long slNumberToConvert, unsigned char ucStyle, CHAR *ptrBuf); // take a value and send it as decimal string over the debug interface or put in buffer


Regards

Mark
Title: Re: about the udp use!
Post by: Marco on February 24, 2009, 07:27:44 PM
Thanks for that Mark, I'm sure that will take some load off of the CPU!

Cheers

Marco