I am using FreeRTOS MQTT Agent demo code. It uses xTaskGenericNotifyWait() and xTaskGenericNotify() for MQTT publishing. If I disconnect the Ethernet cable for a time, and then reconnect it, a flurry of activity seems to call xTaskGenericNotify() many times in a row. Afterward, a variable state seems to be confused. I think the problem is in xTaskGenericNotifyWait(); should this
/* Only block if a notification is not already pending. */
if(pxCurrentTCB->ucNotifyState[uxIndexToWait] != taskNOTIFICATION_RECEIVED)
be this
/* Only block if a notification is not already pending. */
if(pxCurrentTCB->ucNotifyState[uxIndexToWait] != taskWAITING_NOTIFICATION)
It seems to correct itself after restoring my Ethernet connection if I make the above code change.
Something is very funny about your condition, as there is no way that the task can be waiting for the notification when it is testing to see if it needs to be blocking. Your version will cause the program to block and wait for a notification, even if a notification is already pending.
So we’re on the same page, I believe there are 3 enumerations involved here: taskNOT_WAITING_NOTIFICATION, taskWAITING_NOTIFICATION and taskNOTIFICATION_RECEIVED. Look at the code comment above: “Only block if a notification is not already pending.” taskWAITING_NOTIFICATION is used when a notification is pending, no? That’s why I thought that if-statement should test for taskWAITING_NOTIFICATION instead of taskNOTIFICATION_RECEIVED.
I am using the NXP MCUXpresso SDK that includes FreeRTOS and FreeRTOS’ MQTT/OTA libraries. My task calls prvMqttPublish() which sends out an MQTT publish message and then calls xTaskGenericNotifyWait() which self-blocks/yields to other tasks. When the MQTT acknowledgement comes in, the MQTT Agent task handles it with prvMQTTAgentCmdCompleteCallback(), which calls xTaskNotify() to send a notification to unblock my task. This works fine until the Ethernet cable is unplugged during several calls to prvMqttPublish().
Once plugged back in, I think the MQTT Agent sends out multiple publishes at once because they were still sitting in a message queue. Afterward, I can see that pxCurrentTCB->ucNotifyState[0] is left being set to taskNOTIFICATION_RECEIVED, instead of taskNOT_WAITING_NOTIFICATION. It looks like this happens because xTaskGenericNotify() is called many times to handle all of the incoming MQTT acknowledgements, whereas the function xTaskGenericNotifyWait() was called multiple times WHILE the Ethernet cable was unplugged, causing each notification request to simply timeout.
The 2 functions (xTaskNotify & xTaskNotifyWait) are short so it may be easier to follow this by reading them, but it’s the unplugging of the cable that sets all of this in motion. I assume any hardware platform can recreate this issue if it’s tested in the same manner. The symptom for me is that the UART will spit out an MQTT error when there is none.
I am not familiar with MQTT, so can’t answer about its operation. As to the notifications, there are 3 states.
The task is blocked, waiting for the notification, this is entered on a call to wait, if no notifications that match have been received.
The task isn’t block, and no notifications have been received since the last wait. This is the state we go to when we are awoken.
The task isn’t block, but a notification has been receive so a wait after this won’t block.
Note, that if several notifications are given before waiting, the one wait will “clear” all the notifications, so the task needs to check for all the possible notifications before waiting again.
The NotificationTake allows the testing of the value, and if a needed value is in the notification value returns without waiting (normally removing the bits/count in the value in the process).
This might be your problem with MQTT, perhaps you missed a notification because of the multiple notifications being signaled.
BUT, the MQTT publish requests were queued up anyway and are ready to go. Once the cable is re-plugged, no task is waiting for a notification anymore but many do arrive from xTaskNotify(), one for each incoming MQTT acknowledgement. So that leaves the notifyication state as taskNOTIFICATION_RECEIVED because there’s no task waiting to clear that state. Ten minutes later, my next MQTT publish will act funny because an unrelated notification was received before, and it is still sitting there.
Since I understand the idea behind feature #3 in the tasks.c, I guess I’ll have to modify FreeRTOS’ demo (prvMqttPublish()) to clear the notifcation state right before the next MQTT publish. Thank you for your help.