Passing and receiving Data to and from device

220 views
asked Dec 20, 2018 in System Integration by Treeview

Hello

This problem might(?) not be directly connected to Embedded Wizard. But anyways:

I am using an OS on the device and I did read the documentation about calling function from the UI and getting data from the UI. Since I use an OS I am kind of familiar with using queues to pass and receive actions.

The thing that startles me is this: For every action started by the UI I need a queue that passes the required action to the device application (with additional data, that's why it has its own queue). And one additional queue to tell the UI that the task has been performed and some data to see the result.

I ended up with a whole lot of different queues halfway through the project. That just doesn't feel right. Am I doing something wrong? Or is this considered the appropriate way?

 

For example: A button press results in a call of this particular method:

bool DeviceDriver_RequestSomeDataOfTheNVRAM ( uint32_t id, uint32_t amount )
{
	if ( dataRequestQueue == NULL )
	{
		return false;
	}
	DeviceDriver_DataRequest_t* item = osMailCAlloc ( dataRequestQueue, 0 );
	if ( item == NULL )
	{
		return false;
	}
	item->Id = id;
	item->Amount = amount;
	if ( osMailPut ( dataRequestQueue, item ) != osOK )
	{
		osMailFree ( dataRequestQueue, item );
		return false;
	}
	return true;
}

Then the device application frequently checks this:
 

DeviceDriver_DataRequest_t DeviceDriver_CheckForDataRequests ( void )
{
	DeviceDriver_DataRequest_t request = { .Id = -1 };
	if(dataRequestQueue == NULL)
	{
		return request;
	}
	osEvent event = osMailGet ( dataRequestQueue, 0 );
	if ( event.status != osEventMail )
	{
		return request;
	}
	DeviceDriver_DataRequest_t * item = event.value.p;
	memcpy(&request, item, sizeof(request));
	osMailFree ( dataRequestQueue, item );
	return request;
}

... performs the appropriate action and calls:
 

bool DeviceDriver_AnswerArrivalRequest ( DeviceDriver_DataRequest_t request, const char* name )
{
 if ( dataReqeustAnswerQueue == NULL )
 {
	 return false;
 }
 dataReqeustAnswer_t* item = osMailCAlloc ( dataReqeustAnswerQueue, 0 );
 if ( item == NULL )
 {
	 return false;
 }
 item->Request = request;
 memcpy(item->Name, name, MIN ( strlen ( name ), sizeof ( item->Name ) ) );
 if ( osMailPut ( dataReqeustAnswerQueue, item) != osOK )
 {
	 osMailFree ( dataReqeustAnswerQueue, item );
	 return false;
 }
 return true;
}

... and within the "DeviceDriver_ProcessData" (derrived from the template):
 

int DeviceDriver_ProcessData( void )
{
  // ...
  
  event = osMailGet ( dataRequestAnswerQueue, 0 );
  if ( event.status == osEventMail )
  {
  	dataRequestAnswer_t* item = (void*)event.value.p;
  	DataNamesDeviceClass_UpdateProperty ( nameObject, item->Request.Id, item->Name );
  	osMailFree ( dataRequestAnswerQueue, item );
  }

  // ...
}

That is 2 Queues for just one action. And there are about 30 different actions all with parameters to send and receive to the device application. That cannot be right, can it?

Hope this helps some other souls out there for the device implementation.

Thanks for the advice in advance.

1 Answer

0 votes
answered Dec 30, 2018 by Paul Banach
Hello,

your described application case is not seldom. Using queues to decouple UI from background threads is a good approach. I would, however, limit to use two queues for the entire communication between the UI and the device application:

Queue 1: to send commands from the UI to the device (the COMMAND queue)

Queue 2: to receive answers from the device (the ANSWER queue)

I would furthermore specify a kind of protocol how messages should be stored within the queues. For example, the command messages in the queue 1 could contain an id and additional parameters. The number of parameters may differ depending in the command id. Similarly the answers (messages in the second queue) should be structured according to a predetermined protocol.

If you prefer, you can format the messages directly as strings. For example, if the user presses the button X, you feed the command queue with the string "BUTTON_X_PRESSED". Thereupon the device can process the command and send an answer in the second queue, e.g. "ENGINE_Y_STOPPED". Thus using strings for the communication is also a kind of protocol. All you need in this case is to format the strings when you feed a queue with a message and then parse the string again when you receive a message from the queue.

The usage of queues is ideal to decouple the UI from a background thread. If your device has more than one background thread, then you can manage for every background thread an individual pair of Command/Answer queues.

Hopefully this short explanation helps you further.

Best regards and Happy New Year 2019!

Paul Banach

Ask Embedded Wizard

Welcome to the question and answer site for Embedded Wizard users and UI developers.

Ask your question and receive answers from the Embedded Wizard support team or from other members of the community!

Embedded Wizard Website | Privacy Policy | Imprint

...