Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
## Release Notes

### v2.1.1 (In Development)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we normally do this? I think usually we wait to add changelog entries till release pr


* New Features
- Automatic client keep-alive: the core client now sends a `PINGREQ` from
`MqttClient_WaitMessage` / `MqttClient_WaitMessage_ex` once the outbound
link has been idle for about three quarters of the negotiated keep-alive
interval, so the application no longer has to schedule pings itself and
the ping reaches the broker before the deadline. Active whenever a
non-zero keep-alive is set in `CONNECT`, and honors a v5 CONNACK Server
Keep Alive override. In a blocking build a keep-alive ping left
unanswered surfaces from `MqttClient_WaitMessage` as
`MQTT_CODE_ERROR_NETWORK` rather than a timeout, so a dead link is
distinguishable from an idle one; under `WOLFMQTT_NONBLOCK` the
application remains responsible for its own liveness deadline. Because the
ping round-trip runs inline using `cmd_timeout_ms`, a
`MqttClient_WaitMessage` call that starts a keep-alive can take up to
`cmd_timeout_ms` longer to return, so poll with a `timeout_ms` shorter
than the keep-alive interval. The time source is the
compile-time macro `WOLFMQTT_GET_TIME_S()` (defaults to `time(NULL)`,
overridable in `user_settings.h`); define `WOLFMQTT_NO_TIME` to compile
the scheduler out on clock-less targets, where the explicit
`MqttClient_Ping` APIs still work. An application that already sends its
own `PINGREQ` can disable the core scheduler at runtime - without changing
the negotiated keep-alive - by setting `MQTT_CLIENT_FLAG_NO_AUTO_KEEPALIVE`
with `MqttClient_Flags` before connecting, avoiding duplicate pings. The
`mqttclient` example now relies on the automatic ping, keeping its
previous manual keep-alive loop under `WOLFMQTT_NO_TIME` (#501)

### v2.1.0 (07/02/2026)
Release 2.1.0 has been developed according to wolfSSL's development and QA
process (see link below) and successfully passed the quality criteria.
Expand Down
6 changes: 6 additions & 0 deletions examples/aws/awsiot.c
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,7 @@ int awsiot_test(MQTTCtx *mqttCtx)
else
#endif
if (rc == MQTT_CODE_ERROR_TIMEOUT) {
#ifdef WOLFMQTT_NO_TIME
/* Keep Alive */
PRINTF("Keep-alive timeout, sending ping");

Expand All @@ -819,6 +820,11 @@ int awsiot_test(MQTTCtx *mqttCtx)
MqttClient_ReturnCodeToString(rc), rc);
break;
}
#else
/* The core client sends keep-alive PINGREQ automatically, so
* an idle timeout just means no message arrived. */
rc = MQTT_CODE_SUCCESS;
#endif
}
else if (rc != MQTT_CODE_SUCCESS) {
/* There was an error */
Expand Down
15 changes: 15 additions & 0 deletions examples/azure/azureiothub.c
Original file line number Diff line number Diff line change
Expand Up @@ -514,10 +514,25 @@ int azureiothub_test(MQTTCtx *mqttCtx)
else
#endif
if (rc == MQTT_CODE_ERROR_TIMEOUT) {
#ifdef WOLFMQTT_NO_TIME
/* Keep Alive */
PRINTF("Keep-alive timeout, sending ping");
mqttCtx->stat = WMQ_PING;
break;
#else
/* The core client sends keep-alive PINGREQ automatically, so
* an idle timeout just means no message arrived; keep
* waiting. In test mode this example has no inbound message
* and previously terminated via the manual ping, so exit
* here instead of looping forever. This mTestDone assignment
* is deliberately not present in awsiot/fwclient/mqttclient:
* those end test mode another way (a received message, a
* pre-existing test-mode flag, or a top-of-loop break). */
if (mqttCtx->test_mode) {
mTestDone = 1;
}
rc = MQTT_CODE_SUCCESS;
#endif
}
else if (rc != MQTT_CODE_SUCCESS) {
/* There was an error */
Expand Down
6 changes: 6 additions & 0 deletions examples/firmware/fwclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ int fwclient_test(MQTTCtx *mqttCtx)
PRINTF("Timeout in test mode, exit early!");
mTestDone = 1;
}
#ifdef WOLFMQTT_NO_TIME
/* Keep Alive */
PRINTF("Keep-alive timeout, sending ping");

Expand All @@ -436,6 +437,11 @@ int fwclient_test(MQTTCtx *mqttCtx)
MqttClient_ReturnCodeToString(rc), rc);
break;
}
#else
/* The core client sends keep-alive PINGREQ automatically, so
* an idle timeout just means no message arrived. */
rc = MQTT_CODE_SUCCESS;
#endif
}
else if (rc != MQTT_CODE_SUCCESS) {
/* There was an error */
Expand Down
9 changes: 8 additions & 1 deletion examples/mqttclient/mqttclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,9 @@ int mqttclient_test(MQTTCtx *mqttCtx)
}
#endif
else if (rc == MQTT_CODE_ERROR_TIMEOUT) {
/* Keep Alive */
#ifdef WOLFMQTT_NO_TIME
/* Automatic keep-alive is compiled out (no time source), so the
* application schedules the PINGREQ itself on an idle timeout. */
PRINTF("Keep-alive timeout, sending ping");

rc = MqttClient_Ping_ex(&mqttCtx->client, &mqttCtx->ping);
Expand All @@ -607,6 +609,11 @@ int mqttclient_test(MQTTCtx *mqttCtx)
MqttClient_ReturnCodeToString(rc), rc);
break;
}
#else
/* The core client sends keep-alive PINGREQ automatically, so an
* idle timeout just means no message arrived; keep waiting. */
rc = MQTT_CODE_SUCCESS;
#endif
Comment thread
embhorn marked this conversation as resolved.
}
else if (rc != MQTT_CODE_SUCCESS) {
/* There was an error */
Expand Down
8 changes: 7 additions & 1 deletion examples/mqttsimple/mqttsimple.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,12 +449,18 @@ int mqttsimple_test(void)
rc = MqttClient_WaitMessage_ex(&mClient, &mqttObj, MQTT_CMD_TIMEOUT_MS);

if (rc == MQTT_CODE_ERROR_TIMEOUT) {
/* send keep-alive ping */
#ifdef WOLFMQTT_NO_TIME
/* Automatic keep-alive is compiled out, so send the ping here. */
rc = MqttClient_Ping_ex(&mClient, &mqttObj.ping);
if (rc != MQTT_CODE_SUCCESS) {
break;
}
PRINTF("MQTT Keep-Alive Ping");
#else
/* The core client sends keep-alive PINGREQ automatically, so an
* idle timeout just means no message arrived; keep waiting. */
rc = MQTT_CODE_SUCCESS;
#endif
}
else if (rc != MQTT_CODE_SUCCESS) {
break;
Expand Down
34 changes: 31 additions & 3 deletions examples/multithread/multithread.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ static int mNumMsgsDone;
#endif

static wm_Sem mtLock; /* Protect "packetId" and "stop" */
#ifdef WOLFMQTT_NO_TIME
/* Only needed when the core auto keep-alive is compiled out; otherwise
* MqttClient_WaitMessage_ex schedules the PINGREQ and this dedicated ping
* thread would be a second keep-alive owner on the same connection. */
static wm_Sem pingSignal;
#endif

static MQTTCtx gMqttCtx;

Expand Down Expand Up @@ -269,11 +274,13 @@ static int multithread_test_init(MQTTCtx *mqttCtx)
if (rc != 0) {
client_exit(mqttCtx);
}
#ifdef WOLFMQTT_NO_TIME
rc = wm_SemInit(&pingSignal);
if (rc != 0) {
wm_SemFree(&mtLock);
client_exit(mqttCtx);
}
#endif

PRINTF("MQTT Client: QoS %d, Use TLS %d", mqttCtx->qos,
mqttCtx->use_tls);
Expand Down Expand Up @@ -393,7 +400,9 @@ static int multithread_test_finish(MQTTCtx *mqttCtx)
{
client_disconnect(mqttCtx);

#ifdef WOLFMQTT_NO_TIME
wm_SemFree(&pingSignal);
#endif
wm_SemFree(&mtLock);

PRINTF("MQTT Client Done: %d", mqttCtx->return_code);
Expand Down Expand Up @@ -518,13 +527,15 @@ static void *waitMessage_task(void *param)
MQTTCtx *mqttCtx = (MQTTCtx*)param;
word32 startSec;
word32 cmd_timeout_ms = mqttCtx->cmd_timeout_ms;
#ifdef WOLFMQTT_NO_TIME
int needsUnlock = 0;

if (wm_SemLock(&pingSignal) != 0) { /* default to locked */
THREAD_EXIT(0);
}

needsUnlock = 1;
#endif

/* Read Loop */
PRINTF("MQTT Waiting for message...");
Expand Down Expand Up @@ -602,10 +613,16 @@ static void *waitMessage_task(void *param)
break;
}

/* Keep Alive handled in ping thread */
/* Signal keep alive thread */
#ifdef WOLFMQTT_NO_TIME
/* Core auto keep-alive is compiled out: wake the dedicated ping
* thread to send the PINGREQ. */
wm_SemUnlock(&pingSignal);
needsUnlock = 0;
#else
/* Core auto keep-alive already scheduled the PINGREQ inside
* MqttClient_WaitMessage_ex; an idle timeout just means no message
* arrived, so keep waiting. */
#endif
}
else if (rc != MQTT_CODE_SUCCESS) {
/* There was an error */
Expand All @@ -617,9 +634,11 @@ static void *waitMessage_task(void *param)
} while (!mqtt_stop_get());

mqttCtx->return_code = rc;
#ifdef WOLFMQTT_NO_TIME
if (needsUnlock) {
wm_SemUnlock(&pingSignal); /* wake ping thread */
}
#endif

THREAD_EXIT(0);
}
Expand Down Expand Up @@ -681,6 +700,12 @@ static void *publish_task(void *param)
THREAD_EXIT(0);
}

#ifdef WOLFMQTT_NO_TIME
/* Dedicated keep-alive ping thread. Only compiled when the core auto
* keep-alive is disabled (WOLFMQTT_NO_TIME); otherwise
* MqttClient_WaitMessage_ex sends the PINGREQ and a second owner here would
* put redundant PINGREQs and PINGRESP waiters on the same connection. The
* reader thread signals this thread on an idle timeout. */
#ifdef USE_WINDOWS_API
static DWORD WINAPI ping_task( LPVOID param )
#else
Expand Down Expand Up @@ -726,6 +751,7 @@ static void *ping_task(void *param)

THREAD_EXIT(0);
}
#endif /* WOLFMQTT_NO_TIME */

static int unsubscribe_do(MQTTCtx *mqttCtx)
{
Expand Down Expand Up @@ -786,11 +812,13 @@ int multithread_test(MQTTCtx *mqttCtx)
PRINTF("THREAD_CREATE failed: %d", errno);
return -1;
}
/* Ping */
#ifdef WOLFMQTT_NO_TIME
/* Ping (only when core auto keep-alive is compiled out) */
if (THREAD_CREATE(&threadList[threadCount++], ping_task, mqttCtx)) {
PRINTF("THREAD_CREATE failed: %d", errno);
return -1;
}
#endif

/* Create threads that publish unique messages */
for (i = 0; i < NUM_PUB_TASKS; i++) {
Expand Down
10 changes: 9 additions & 1 deletion examples/nbclient/nbclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -524,11 +524,19 @@ int mqttclient_test(MQTTCtx *mqttCtx)
else
#endif
if (rc == MQTT_CODE_ERROR_TIMEOUT) {
/* Need to send keep-alive ping */
#ifdef WOLFMQTT_NO_TIME
/* Automatic keep-alive is compiled out, so the application
* schedules the PINGREQ itself on an idle timeout. */
PRINTF("Keep-alive timeout, sending ping");
rc = MQTT_CODE_CONTINUE;
mqttCtx->stat = WMQ_PING;
return rc;
#else
/* The core client schedules keep-alive PINGREQ itself, so
* an idle timeout just means no message arrived. */
rc = MQTT_CODE_CONTINUE;
return rc;
#endif
}
else if (rc != MQTT_CODE_SUCCESS) {
/* There was an error */
Expand Down
6 changes: 6 additions & 0 deletions examples/pub-sub/mqtt-sub.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ int sub_client(MQTTCtx *mqttCtx)
}
#endif
else if (rc == MQTT_CODE_ERROR_TIMEOUT) {
#ifdef WOLFMQTT_NO_TIME
/* Keep Alive */
if (mqttCtx->debug_on) {
PRINTF("Keep-alive timeout, sending ping");
Expand All @@ -530,6 +531,11 @@ int sub_client(MQTTCtx *mqttCtx)
MqttClient_ReturnCodeToString(rc), rc);
break;
}
#else
/* The core client sends keep-alive PINGREQ automatically, so an
* idle timeout just means no message arrived. */
rc = MQTT_CODE_SUCCESS;
#endif
}
#ifdef WOLFMQTT_NONBLOCK
else if (rc == MQTT_CODE_CONTINUE) {
Expand Down
14 changes: 14 additions & 0 deletions examples/wiot/wiot.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ int wiot_test(MQTTCtx *mqttCtx)
else
#endif
if (rc == MQTT_CODE_ERROR_TIMEOUT) {
#ifdef WOLFMQTT_NO_TIME
/* Keep Alive */
PRINTF("Keep-alive timeout, sending ping");

Expand All @@ -342,6 +343,19 @@ int wiot_test(MQTTCtx *mqttCtx)
MqttClient_ReturnCodeToString(rc), rc);
break;
}
#else
/* The core client sends keep-alive PINGREQ automatically, so an
* idle timeout just means no message arrived; keep waiting. In test
* mode this example has no inbound message and previously terminated
* via the manual ping, so exit here instead of looping forever. This
* mTestDone assignment is deliberately not present in awsiot/fwclient/
* mqttclient: those end test mode another way (a received message, a
* pre-existing test-mode flag, or a top-of-loop break). */
if (mqttCtx->test_mode) {
mTestDone = 1;
}
rc = MQTT_CODE_SUCCESS;
#endif
}
else if (rc != MQTT_CODE_SUCCESS) {
/* There was an error */
Expand Down
Loading
Loading