Last time I get into an embedded project I realised that I am getting tired of USART interface errors and I should definetely switch to a better organized tracing system. While sending debug data or any relevant data through network interface you can use an open source  syslog server available through google. I used Syslog Server located in this link. So when you finish your tests you can direct your results to a real syslog server such as native unix syslog available in all linux distros.

So, the deal is, since you will work on an embeded environment, you don’t have too much memory to store syslog entries until the syslog service push the logs to the remote server, so I decided to use linked list in order to minimize overhead and store every log in its variable length storage unit. The main structure of the variable is:

typedef struct _SYSLOGMESSAGE {
	char * text;
	unsigned int size;
	struct _SYSLOGMESSAGE *_next;
	} SYSLOGMESSAGE;

What this structure telling us is every log message coming to the queue is stored in an arbitrary memory allocation and linked to the next message within itself. The advantage to use linked list is, you don’t have to create a static length array even every row is created dynamically, every message in syslog may vary from 10 characters to say 255 characters and obviously you need to delete all stored messages from the queue with their coming order. When the logger start pushing the messages qeued on RAM, a temproray pointer will point to the first message, process the messsage and before dispatching the message the pointer will set itself to the next message by using _next field inside the structure. With this method there is no need to allocate a large array block in memory.

Global SysLog Function to be used by all processes:

In the header file (syslog.h) you might want to declare the function as:

void SysLog(const char *msg, int severity);

and other necessary declerations as well:

#define LOG_LOCAL0      (16<<3) /* your atmel will send messages with this ID */
#define LOG_LOCAL1      (17<<3) /* reserved for local use */
#define LOG_LOCAL2      (18<<3) /* reserved for local use */

Please refer to syslog RFC for further information about the lines above, since we need LOCAL0 only, the definitions are enough. You may want to seperate LOCAL0 messages in the syslogd server and you can find examples in the net. syslog subsystem uses UDP to send and receive data so we don’t need to get into deep TCP/IP programming routines.

There will be two routines to complete syslog for Atmel, we can call them as sink & the source (syslog). Source will be called by other processes to send syslog messages to the syslog queue, and sink will push them to syslogd server in a period of time.

The source:

void SysLog(const char *msg, int severity)
{
	semaphore = true; // tell all processes to wait until I finish
	vTaskDelay( 10 ); // add some delay
	char *tmptext = ( char * )malloc( sizeof( char )*MESSAGESIZE ); // Allocate space with the size of msg text
	if(tmptext!=NULL) // If allocated
	{
		time_t now;unsigned int n=0;
		message = (SYSLOGMESSAGE*)malloc(sizeof(SYSLOGMESSAGE)); // Allocate space with the size of syslog
		if(message!=NULL) // If allocated
		{
			n=sprintf(tmptext, "<%d>%.15s %s", LOG_LOCAL0 | severity, ctime(&now) + 4, msg); // format message
			tmptext[n] = '\0'; // add trailing EOF descriptor
			message->_next = NULL; // this is the last message (we know it)
			message->text = (char*)malloc(n); // allocate message
			memcpy(message->text,tmptext,n); // set pointer of the allocated message
			free(tmptext); // free temprorary message space
			message->size = n; // send message size
			if(curwp!=NULL) // If this is not the first message in the queue
			{
				curwp->_next = message; // set previos messages _next to this message
				curwp = message; // set current message pointer to this message
				curwp->_next = NULL; // set next to NULL to be sure
			}
			else // else
			{
				first = message; // else this is the first message ever
				curwp = first; // set curwp pointer to this message also
			}

		} else NAKED_TRACE_COM2("cannot allocate memory for message");			

	} else NAKED_TRACE_COM2("cannot allocate memory for tmp");		
	semaphore = false; // free semaphore
}

As described, the source (syslog() function) will be responsible for adding syslog messages to the syslog queue.

The Sink:

The sink is operated by FreeRTOS, for simplicity we set empty BasicTFTP.c & BasicTFTP.h in Evk1100 Control Panel demo example, be use to set TFTP_USED=1 symbol in project properties.

Here is the code:

portTASK_FUNCTION( vBasicTFTPServer, pvParameters ){
	SYSLOGMESSAGE *tmp; // temprorary syslog message
	first = NULL;
	curwp= NULL;

	struct netconn *conn; 
	struct ip_addr addr;
	int port;
	struct netbuf *buf;
	char * data;

	while (1) {
		while(semaphore); // wait until new syslog message drops to queue
		if(first!=NULL) // If any messages in the queue
		{
			do
			{
				tmp = first; // set temproray pointer to the first message
				if(first->text!=NULL)
				{
					conn=netconn_new(NETCONN_UDP); // create new connection
					if(conn!=NULL)
					{
						IP4_ADDR(&addr,192,168,2,5); port=514; // set your IP address of the syslogd server & port here
						if(netconn_connect(conn, &addr, port)==ERR_OK)
						{
							buf=netbuf_new();
							data =netbuf_alloc(buf, first->size);
							memcpy (data, first->text, first->size);
							netconn_send(conn, buf);
							netbuf_delete(buf);
						} else NAKED_TRACE_COM2("netconn connect error");
						netconn_delete(conn);
					} else NAKED_TRACE_COM2("netconn error");

					if(tmp->text!=NULL) free(tmp->text); // empty message allocation
				}
				first=first->_next; // set next message as first
				free(tmp); // delete vanished message structure

				if(first==NULL) curwp=NULL; // if no messages set curwp to NULL
			} while(tmp->_next!=NULL);
		} 

		vTaskDelay( 100 );  //some delay!
	}
}

For a complete source code please follow this link:

[wpdm_file id=2]

Leave a Reply

Your email address will not be published. Required fields are marked *