MQTT Agent Blocking Socket Support for Low Power Functionality

Hi there,

I am currently porting the MQTT agent to a new low power wifi MCU.

Our previous stack had support for blocking sockets. At a high level, 1 task would make a blocking call to our MCU’s tcp receive() function, so that we can receive updates at any time from our server and the MCU’s stack will let the chip fall into low power while there is no network activity. Another task would handle commands to interact with the socket, such as closing the socket if the networking layer got reset, or sending data on the socket.

It seems that MQTT agent does not work this way, since if I configure the socket to have an indefinite timeout waiting to rx some data, won’t this also cause the MQTTAgent queue to never be processed? Thus, it won’t be able to process publish messages from the queue and do other useful things. Shouldn’t the networking socket recv() call be in a separate thread (data) from the mqtt agent command queue (commands?).

Please let me know if my understanding is not correct on the MQTT agent. As it stands, it seems one cannot use MQTT agent with blocking socket reads and thus is not useful for low power apps. Thanks.

Hi Schoeler,

From the TransportRecv_t document, it is suggested to implement the transport interface with the following recommendation:

  • Transport receive implementation does NOT block when requested to read a single byte.
  • Transport receive implementation MAY block for a timeout period when it is requested to read more than 1 byte.

We would like to know the following questions:

  • Is it possible to update the transport receive implementation with a specified timeout?
  • Can you also evaluate the timeout impact to your low power application to help us better understand your requirement?

At the same time, we will bring back the low power requirement about coreMQTT agent for internal discussion and later reply in this thread.

Hi Fresh,

I’ll use our last MCU as an example for why this is important.

Let’s say the lowest power sleep state takes 300uA of current, and the power state when the MCU is running takes 20mA. It takes the MCU ~50ms to transition from the low power state to the running state.

Therefore, if once per second the MCU has software that requires polling on a socket, the average current goes to .05 * 20mA + 0.95 * 300uA = 1.285 mA Idle current draw. If the software did not require polling to check if there is data to read on the socket (blocking socket functionality), the MCU can remain in its lowest power state (300uA).

So comparing these 2 values, the polling socket with a 1s period is over 4x higher power required vs blocking sockets. For low power applications, this can decrease the life of a product by years, requiring the product to have larger batteries and increasing the system cost for no reason. The problem gets worse if you want a better responsiveness and check the socket twice or four times a second.

FreeRTOS is used by lots of lower power apps. Tickless idle is a core part of it! As such, anything that polls in the system must be eliminated for low power apps.

Let me know if I can help clarify anything.
Thanks.

The MQTT agent is event driven, allowing the task implementing the agent to remain in the Blocked state (not consuming any CPU cycles) whenever there aren’t any events to process.

Events originating from API calls and the MQTT TCP/IP socket arrive on a FreeRTOS queue, which is where the task blocks. If you block on the socket, the task won’t be blocked on the queue, and therefore be unresponsive to anything other than socket events. So, to keep the agent task responsive, set the socket’s receive block time to zero after it connects. Likewise, to keep the agent task responsive to events originating on the socket, send an event to the queue each time data arrives on the socket from the network (unblocking the task so it can process the socket event - basically read the arriving data from the socket). That can be done in the FreeRTOS+TCP stack using a wakeup callback that can post to the queue (or otherwise just abort the task’s block time on the queue if you are careful about race conditions).

I suspect network activity will be the limiting factor for power saving. The TCP/IP stack will wake the MCU each time it processes traffic, even if the processing just determines the traffic is not relevant.

Thanks Richard.

Unfortunately our MCU socket stack doesn’t support a wakeup callback. I’m pretty sure the solution here is to spin up another task that calls socket select() on the socket fd, so that I can kick off a call to ProcessLoop in the the mqtt agent? I think this basically accomplishes the same thing, but requires a bit more overhead.

Should MQTT_AGENT_MAX_EVENT_QUEUE_WAIT_TIME be set to portMAX_DELAY for this kind of setup?

Thanks

Hello @schoeler,

I know that this is a big ask, but worth a shot: would it be possible for you to switch to FreeRTOS+TCP? +TCP does support a wakeup callback as Richard pointed out above.

Yes, that is what I’d propose too.

I think that might not be a good idea if the keep-alive time of your MQTT connection is too low. Imagine that the Keep-alive is 10s and your MQTT Agent doesn’t get any events from the user or the network. In this case, the MQTT server will disconnect your device as you fail to ping the server within 1.5*Keep-alive period.
If you have the maximum possible keep-alive (approx.18.25hrs) and you are sure that you will somehow generate an event to wake up the Agent within that time frame, then I think that you can keep the MQTT_AGENT_MAX_EVENT_QUEUE_WAIT_TIME to portMAX_DELAY.

As a side note, I’d like to highlight that if you have just one task/thread using coreMQTT, then you need not use the Agent. Of course there will be some changes required, but that might be something you want.

Thanks,
Aniruddha

Thanks Aniruddha.

I think in this case, for low power, setting MQTT_AGENT_MAX_EVENT_QUEUE_WAIT_TIME == Keep Alive time for MQTT makes sense.