Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [paho-dev] Design question - how to best approach background tasks for the embedded clients

Hi Richard,

attached is the sample application from the FreeRTOS project I am working with. I do have a good idea of where I want to go from here, now, but I'm happy to accept suggestions... especially if they include the use of undocumented functions ;-)

Ian



On 05/25/2015 05:50 PM, Richard Barry wrote:
Hi Ian,

For ease of use and maintainability reasons it would, in my humble opinion, be best to not only hide the cycle() function, but also the yield() function. That would mean, if yield were called at all, it would be inside the public API functions.

I am still not completely clear as to the purpose of yield(), but suspect if a separate MQTT thread were allowed then yield() would not be required. A background thread would free up processing time by being event driven (important in low power systems that want to sleep), so improve run time resource usage. If it is considered that there is not enough RAM resource for the background thread then consider how much RAM could be saved in each thread using MQTT if the RAM were instead spent in a single MQTT thread. If it were still a problem then there is already a TCP/IP thread, so maybe that could be enhanced to allow user defined extensions - then run the MQTT stuff in there [that last suggestion would not be a quick change!].

I would envisage an application thread using the MQTT thread as a sort of proxy server. The application thread just sends data via the MQTT thread - all the ins and outs of how the data is sent, and QoS level maintained, would be entirely inside the MQTT thread itself. The application thread can sleep until notified by the MQTT thread as to the the success or failure of the send. Likewise when receiving data, the receive can be handled inside the MQTT thread, and then a callback function (or queue, or whatever) used to send received data to application threads that need it.

If you can we suggest one send or receive scenario to concentrate on, and describe the sequence used to implement the scenario that involves calls to yield(), then perhaps I could follow up with a suggestion of how it could possibly be implemented without the yield().

Regards,
Richard.

+ http://www.FreeRTOS.org
Designed for simplicity. More than 113000 downloads in 2014.

+ http://www.FreeRTOS.org/plus
IoT, Trace, Certification, FAT FS, TCP/IP, Training, and more...



On 22/05/2015 12:36, Ian Craggs wrote:
Hello all,

as I've been updating the C "high level" embedded client for FreeRTOS:

(http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.embedded-c.git/commit/?h=develop)


I've been pondering how to best have background tasks completed.   I
originally envisaged two styles of APIs, similar to the standard Paho C
client libraries, synchronous and asynchronous.  I haven't yet created
the asynchronous version, but that would use a background thread, at the
cost of using more resources.

The "synchronous" or blocking API would be easy to use, and all
processing would take place on the application's thread. Because no
separate threads were involved, this would both simplify implementation
and reduce resource usage.  To allow messages to be received, there is a
yield() call, which takes as its parameter a time to run.  The API also
has a a function (or method) called cycle(), which processes one
incoming MQTT packet.  Yield() just calls cycle() under the covers.  I
didn't originally intend to make cycle() a public function.

The problem with yield() is that it might spend most of its time doing
nothing.  An MQTT packet might arrive at the end of the timeout period,
and not get processed until the next call to yield().   One solution is
to make cycle() a public function and allow the application to
interleave application with MQTT processing at a fine-grained level.

For FreeRTOS I was considering also a background task to be started. But
this would still have the negative effects of increased resource usage
and complexity as synchronization of the common data structures would be
needed.  So I'm reconsidering this once more, and thinking of leaving
the background thread to an (almost) totally non-blocking API.

I had also been thinking of changing the semantics of yield() in some
way to be more event driven.  But I don't know whether this would buy
any advantages over making cycle() a public function.

Thoughts?

_______________________________________________
paho-dev mailing list
paho-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/paho-dev

--
Ian Craggs
icraggs@xxxxxxxxxx                 IBM United Kingdom
Paho Project Lead; Committer on Mosquitto

/* Standard includes. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"

#include "MQTTClient.h"


void messageArrived(MessageData* data)
{
	printf("Message arrived on topic %.*s: %.*s\n", data->topicName->lenstring.len, data->topicName->lenstring.data,
		data->message->payloadlen, data->message->payload);
}

static void prvMQTTEchoTask(void *pvParameters)
{
	/* connect to m2m.eclipse.org, subscribe to a topic, send and receive messages regularly every 1 sec */
	MQTTClient client;
	Network network;
	unsigned char sendbuf[80], readbuf[80];
	int rc = 0, 
		count = 0;
	MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;

	pvParameters = 0;
	NewNetwork(&network);
	MQTTClientInit(&client, &network, 30000, sendbuf, sizeof(sendbuf), readbuf, sizeof(readbuf));

	char* address = "iot.eclipse.org";
	if ((rc = ConnectNetwork(&network, address, 1883)) != 0)
		printf("Return code from network connect is %d\n", rc);

	connectData.MQTTVersion = 3;
	connectData.clientID.cstring = "FreeRTOS_sample";

	if ((rc = MQTTConnect(&client, &connectData)) != 0)
		printf("Return code from MQTT connect is %d\n", rc);
	else
		printf("MQTT Connected\n");

	if ((rc = MQTTSubscribe(&client, "FreeRTOS/sample/#", 2, messageArrived)) != 0)
		printf("Return code from MQTT subscribe is %d\n", rc);

	while (++count)
	{
		MQTTMessage message;
		char payload[30];

		message.qos = 1;
		message.retained = 0;
		message.payload = payload;
		sprintf(payload, "message number %d", count);
		message.payloadlen = strlen(payload);

		if ((rc = MQTTPublish(&client, "FreeRTOS/sample/a", &message)) != 0)
			printf("Return code from MQTT publish is %d\n", rc);
		if ((rc = MQTTYield(&client, 1000)) != 0)
			printf("Return code from yield is %d\n", rc);
	}

	/* do not return */
}


void vStartMQTTTasks(uint16_t usTaskStackSize, UBaseType_t uxTaskPriority)
{
	BaseType_t x = 0L;

	xTaskCreate(prvMQTTEchoTask,	/* The function that implements the task. */
			"MQTTEcho0",			/* Just a text name for the task to aid debugging. */
			usTaskStackSize,	/* The stack size is defined in FreeRTOSIPConfig.h. */
			(void *)x,		/* The task parameter, not used in this case. */
			uxTaskPriority,		/* The priority assigned to the task is defined in FreeRTOSConfig.h. */
			NULL);				/* The task handle is not used. */
}
/*-----------------------------------------------------------*/



Back to the top