µTasker Forum
µTasker Forum => µTasker general => Topic started 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???
-
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:
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
// 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
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.
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:
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:
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
-
Thank you very much ! mark !
-
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
-
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
-
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
-
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
-
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
-
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
-
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
-
May we ask what language/compiler you were going to use for the PC software?
-
can you clarify for me what fnReleaseUDP_socket does please?
What actually gets freed - it looks like the socket handle is left alone?
-
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
-
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
-
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
-
Thanks a bunch! I think i understand now whats going on there. I'm going to try that right now!
Cheers
Marco
-
Hi Mark
Sorry, I'm stuck again. Ok this is what I got so far, how ever the compiler sais fnUDPListner is not declared.
#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.
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
-
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
-
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.
#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
-
Hi Marco
static UDP_MESSAGE *ptrUDP_Frame;
ptrUDP_Frame = uMalloc(sizeof(UDP_MESSAGE)); // get some memory for UDP frame
Regards
Mark
-
Hi Mark
I have actually tried "static UDP_MESSAGE *ptrUDP_Frame;" im getting an error "C2540: Expected: ,". I'm not sure why that is.
#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
-
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
-
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.
#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
-
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);
#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
-
Hi Mark
Thanks for that I realy apprechiate the help! Am i suposed to remove the following from TaskConfig.h?
{ "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
-
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
-
Thank you so much! It works, I realy apprechiate all the help!
Thanks again
Marco
-
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.
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.
// <<<<<<<<<<<<< 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.
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
-
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
-
Thanks for that Mark, I'm sure that will take some load off of the CPU!
Cheers
Marco