This guide helps you migrate your existing coreMQTT v2.3.1 (MQTT v3.1.1) application to use the new coreMQTT v5.0.0 library. MQTT v5.0 introduces significant enhancements including properties, enhanced reason codes, bidirectional DISCONNECT packets, and improved error handling.
Key Philosophy: The coreMQTT v5.0.0 library is designed to be backward compatible in usage patterns. If you don't want to use MQTT v5.0 features, you can pass NULL for property parameters and the library will work similarly to v3.1.1.
See also: For a complete list of every changed function signature (including serialization APIs), see the coreMQTT Migration Guide.
- Quick Start: Minimal Changes
- API Changes Summary
- Detailed Migration Steps
- Using MQTT v5.0 Properties
- Enhanced Features in v5.0
- Common Migration Patterns
- Troubleshooting
If you want to migrate with minimal code changes and not use v5.0 features, here's what you need to do:
// No change needed - same header
#include "core_mqtt.h"MQTT_Connect:
// coreMQTT v2.3.1 (MQTT v3.1.1)
status = MQTT_Connect(&context, &connectInfo, &willInfo, timeoutMs, &sessionPresent);
// coreMQTT v5.0.0 - Add two NULL parameters for properties
status = MQTT_Connect(&context, &connectInfo, &willInfo, timeoutMs, &sessionPresent,
NULL, // Connect properties (optional)
NULL); // Will properties (optional)MQTT_Subscribe:
// coreMQTT v2.3.1 (MQTT v3.1.1)
status = MQTT_Subscribe(&context, pSubscriptionList, subscriptionCount, packetId);
// coreMQTT v5.0.0 - Add NULL parameter for properties
status = MQTT_Subscribe(&context, pSubscriptionList, subscriptionCount, packetId,
NULL); // Subscribe properties (optional)MQTT_Publish:
// coreMQTT v2.3.1 (MQTT v3.1.1)
status = MQTT_Publish(&context, &publishInfo, packetId);
// coreMQTT v5.0.0 - Add NULL parameter for properties
status = MQTT_Publish(&context, &publishInfo, packetId,
NULL); // Publish properties (optional)MQTT_Unsubscribe:
// coreMQTT v2.3.1 (MQTT v3.1.1)
status = MQTT_Unsubscribe(&context, pSubscriptionList, subscriptionCount, packetId);
// coreMQTT v5.0.0 - Add NULL parameter for properties
status = MQTT_Unsubscribe(&context, pSubscriptionList, subscriptionCount, packetId,
NULL); // Unsubscribe properties (optional)MQTT_Disconnect:
// coreMQTT v2.3.1 (MQTT v3.1.1)
status = MQTT_Disconnect(&context);
// coreMQTT v5.0.0 - Add parameters for properties and reason code
status = MQTT_Disconnect(&context,
NULL, // Disconnect properties (optional)
NULL); // Reason code (optional)The event callback signature has changed significantly:
// coreMQTT v2.3.1 (MQTT v3.1.1)
void eventCallback(MQTTContext_t *pContext,
MQTTPacketInfo_t *pPacketInfo,
MQTTDeserializedInfo_t *pDeserializedInfo)
{
// Handle events
}
// coreMQTT v5.0.0 - Returns bool and has additional parameters
bool eventCallback(MQTTContext_t *pContext,
MQTTPacketInfo_t *pPacketInfo,
MQTTDeserializedInfo_t *pDeserializedInfo,
MQTTSuccessFailReasonCode_t *pReasonCode, // NEW: Reason code
MQTTPropBuilder_t *pSendPropsBuffer, // NEW: Properties to send
MQTTPropBuilder_t *pGetPropsBuffer) // NEW: Properties received
{
// Handle events
// NOTE: pReasonCode and pSendPropsBuffer will be NULL for terminating
// packets (PUBACK, PUBCOMP, SUBACK, UNSUBACK) since the library does
// not send a response for these. Always check before dereferencing.
return true; // Return true if processed successfully
}The coreMQTT v5.0.0 version adds a buffer for ACK properties:
// coreMQTT v2.3.1 (MQTT v3.1.1)
status = MQTT_InitStatefulQoS(&context,
outgoingPublishRecords, outgoingPublishCount,
incomingPublishRecords, incomingPublishCount);
// coreMQTT v5.0.0 - Add buffer for ACK properties
uint8_t ackPropsBuffer[500];
status = MQTT_InitStatefulQoS(&context,
outgoingPublishRecords, outgoingPublishCount,
incomingPublishRecords, incomingPublishCount,
ackPropsBuffer, // NEW: Buffer for ACK properties
sizeof(ackPropsBuffer)); // NEW: Buffer sizeThe coreMQTT v5.0.0 version adds subscription options:
// coreMQTT v2.3.1 (MQTT v3.1.1)
MQTTSubscribeInfo_t subscribeInfo = {
.qos = MQTTQoS1,
.pTopicFilter = "my/topic",
.topicFilterLength = strlen("my/topic")
};
// coreMQTT v5.0.0 - Add subscription options (use defaults for v3.1.1 behavior)
MQTTSubscribeInfo_t subscribeInfo = {
.qos = MQTTQoS1,
.pTopicFilter = "my/topic",
.topicFilterLength = strlen("my/topic"),
.noLocalOption = false, // NEW: Receive own publishes
.retainAsPublishedOption = false, // NEW: Don't preserve retain flag
.retainHandlingOption = retainSendOnSub // NEW: Send retained messages
};| Function | v2.3.1 Parameters | v5.0.0 Additional Parameters |
|---|---|---|
MQTT_Connect |
5 params | +2: pPropertyBuilder, pWillPropertyBuilder |
MQTT_Subscribe |
4 params | +1: pPropertyBuilder |
MQTT_Publish |
3 params | +1: pPropertyBuilder |
MQTT_Unsubscribe |
4 params | +1: pPropertyBuilder |
MQTT_Disconnect |
1 param | +2: pPropertyBuilder, pReasonCode |
MQTT_InitStatefulQoS |
5 params | +2: pAckPropsBuf, ackPropsBufLength |
All serialization functions now accept property builders:
| Function | v2.3.1 Parameters | v5.0.0 Additional Parameters |
|---|---|---|
MQTT_GetConnectPacketSize |
4 params | +2: pConnectProperties, pWillProperties |
MQTT_SerializeConnect |
4 params | +2: pConnectProperties, pWillProperties |
MQTT_GetSubscribePacketSize |
4 params | +1: pPropertyBuilder |
MQTT_SerializeSubscribe |
5 params | +1: pPropertyBuilder |
MQTT_GetPublishPacketSize |
3 params | +1: pPropertyBuilder |
MQTT_SerializePublish |
4 params | +1: pPropertyBuilder |
MQTT_GetUnsubscribePacketSize |
4 params | +1: pPropertyBuilder |
MQTT_SerializeUnsubscribe |
5 params | +1: pPropertyBuilder |
MQTTPropBuilder_t- Property builder for adding properties to packetsMQTTConnectionProperties_t- Connection properties from CONNECT/CONNACKMQTTReasonCodeInfo_t- Reason codes from ACK packetsMQTTSuccessFailReasonCode_t- Enum for v5.0 reason codesMQTTUserProperty_t- User-defined key-value propertiesMQTTRetainHandling_t- Enum for retain handling options
MQTTEndOfProperties- Reached end of properties while parsingMQTTEventCallbackFailed- Event callback returned false
The MQTT_Init function signature remains the same, but the event callback signature has changed.
// Update your event callback to match the new signature
bool myEventCallback(MQTTContext_t *pContext,
MQTTPacketInfo_t *pPacketInfo,
MQTTDeserializedInfo_t *pDeserializedInfo,
MQTTSuccessFailReasonCode_t *pReasonCode,
MQTTPropBuilder_t *pSendPropsBuffer,
MQTTPropBuilder_t *pGetPropsBuffer)
{
// Your existing logic here
// ...
// Return true to indicate successful processing
return true;
}
// MQTT_Init call remains the same
status = MQTT_Init(&context, &transport, getTimeFunction,
myEventCallback, &networkBuffer);// Allocate buffer for ACK properties
uint8_t ackPropsBuffer[500]; // Size depends on your needs
status = MQTT_InitStatefulQoS(&context,
outgoingPublishRecords, outgoingPublishCount,
incomingPublishRecords, incomingPublishCount,
ackPropsBuffer,
sizeof(ackPropsBuffer));// If not using properties, pass NULL
status = MQTT_Connect(&context, &connectInfo, &willInfo,
timeoutMs, &sessionPresent,
NULL, // No connect properties
NULL); // No will properties// Update subscription info structure
MQTTSubscribeInfo_t subscriptions[NUM_SUBS];
for (int i = 0; i < NUM_SUBS; i++) {
subscriptions[i].qos = MQTTQoS1;
subscriptions[i].pTopicFilter = topics[i];
subscriptions[i].topicFilterLength = strlen(topics[i]);
// Add MQTT v5.0 fields with default values
subscriptions[i].noLocalOption = false;
subscriptions[i].retainAsPublishedOption = false;
subscriptions[i].retainHandlingOption = retainSendOnSub;
}
// Subscribe with NULL properties
status = MQTT_Subscribe(&context, subscriptions, NUM_SUBS, packetId, NULL);// Publish with NULL properties
status = MQTT_Publish(&context, &publishInfo, packetId, NULL);// Disconnect with NULL properties and reason code
status = MQTT_Disconnect(&context, NULL, NULL);If you want to leverage v5.0 features, you'll need to use the property builder API.
- Initialize a property builder:
MQTTPropBuilder_t propBuilder;
uint8_t propBuffer[500];
status = MQTTPropertyBuilder_Init(&propBuilder, propBuffer, sizeof(propBuffer));- Add properties using
MQTTPropAdd_*functions:
// Add session expiry
uint32_t sessionExpiry = 3600; // 1 hour
status = MQTTPropAdd_SessionExpiry(&propBuilder, sessionExpiry,
&(uint8_t){MQTT_PACKET_TYPE_CONNECT});
// Add user property
MQTTUserProperty_t userProp = {
.pKey = "device-id",
.keyLength = strlen("device-id"),
.pValue = "sensor-001",
.valueLength = strlen("sensor-001")
};
status = MQTTPropAdd_UserProp(&propBuilder, &userProp,
&(uint8_t){MQTT_PACKET_TYPE_CONNECT});- Pass the property builder to API functions:
status = MQTT_Connect(&context, &connectInfo, &willInfo, timeoutMs,
&sessionPresent, &propBuilder, NULL);MQTTPropAdd_SessionExpiry()- Session expiry intervalMQTTPropAdd_ReceiveMax()- Maximum inflight messagesMQTTPropAdd_MaxPacketSize()- Maximum packet sizeMQTTPropAdd_TopicAliasMax()- Maximum topic aliasesMQTTPropAdd_RequestRespInfo()- Request response informationMQTTPropAdd_RequestProbInfo()- Request problem informationMQTTPropAdd_AuthMethod()- Authentication methodMQTTPropAdd_AuthData()- Authentication dataMQTTPropAdd_UserProp()- User-defined properties
MQTTPropAdd_WillDelayInterval()- Delay before sending willMQTTPropAdd_PayloadFormat()- Payload format indicatorMQTTPropAdd_MessageExpiry()- Message expiry intervalMQTTPropAdd_ContentType()- Content typeMQTTPropAdd_ResponseTopic()- Response topicMQTTPropAdd_CorrelationData()- Correlation dataMQTTPropAdd_UserProp()- User-defined properties
MQTTPropAdd_PayloadFormat()- Payload format indicatorMQTTPropAdd_MessageExpiry()- Message expiry intervalMQTTPropAdd_TopicAlias()- Topic aliasMQTTPropAdd_ResponseTopic()- Response topicMQTTPropAdd_CorrelationData()- Correlation dataMQTTPropAdd_ContentType()- Content typeMQTTPropAdd_UserProp()- User-defined properties
MQTTPropAdd_SubscriptionId()- Subscription identifierMQTTPropAdd_UserProp()- User-defined properties
MQTTPropAdd_UserProp()- User-defined propertiesMQTTPropAdd_ReasonString()- Human-readable reasonMQTTPropAdd_SessionExpiry()- Session expiry (disconnect only)
In your event callback, use the pGetPropsBuffer parameter to read properties:
bool eventCallback(MQTTContext_t *pContext,
MQTTPacketInfo_t *pPacketInfo,
MQTTDeserializedInfo_t *pDeserializedInfo,
MQTTSuccessFailReasonCode_t *pReasonCode,
MQTTPropBuilder_t *pSendPropsBuffer,
MQTTPropBuilder_t *pGetPropsBuffer)
{
if (pGetPropsBuffer != NULL && pGetPropsBuffer->bufferLength != 0) {
size_t index = 0;
uint8_t propertyType;
// Iterate through properties
while (MQTT_GetNextPropertyType(pGetPropsBuffer, &index, &propertyType)
== MQTTSuccess) {
switch (propertyType) {
case MQTT_SESSION_EXPIRY_ID: {
uint32_t sessionExpiry;
MQTTPropGet_SessionExpiry(pGetPropsBuffer, &index, &sessionExpiry);
// Use sessionExpiry value
break;
}
case MQTT_USER_PROPERTY_ID: {
MQTTUserProperty_t userProp;
MQTTPropGet_UserProp(pGetPropsBuffer, &index, &userProp);
// Use userProp
break;
}
default:
// IMPORTANT: For any property you don't handle, you MUST call
// MQTT_SkipNextProperty to advance the index past it. Without
// this, the index will not move and the loop will never terminate.
MQTT_SkipNextProperty(pGetPropsBuffer, &index);
break;
}
}
}
return true;
}Use these functions to extract properties from incoming packets:
CONNACK Properties:
MQTTPropGet_SessionExpiry()MQTTPropGet_ReceiveMax()MQTTPropGet_MaxQos()MQTTPropGet_RetainAvailable()MQTTPropGet_MaxPacketSize()MQTTPropGet_AssignedClientId()MQTTPropGet_TopicAliasMax()MQTTPropGet_ReasonString()MQTTPropGet_UserProp()MQTTPropGet_WildcardId()MQTTPropGet_SubsIdAvailable()MQTTPropGet_SharedSubAvailable()MQTTPropGet_ServerKeepAlive()MQTTPropGet_ResponseInfo()MQTTPropGet_ServerRef()MQTTPropGet_AuthMethod()MQTTPropGet_AuthData()
PUBLISH Properties:
MQTTPropGet_PayloadFormatIndicator()MQTTPropGet_MessageExpiryInterval()MQTTPropGet_TopicAlias()MQTTPropGet_ResponseTopic()MQTTPropGet_CorrelationData()MQTTPropGet_ContentType()MQTTPropGet_SubscriptionId()MQTTPropGet_UserProp()
ACK Properties:
MQTTPropGet_ReasonString()MQTTPropGet_UserProp()
MQTT v5.0 provides detailed reason codes for all ACK packets. Access them via pDeserializedInfo->pReasonCode:
if (pPacketInfo->type == MQTT_PACKET_TYPE_SUBACK) {
if (pDeserializedInfo->pReasonCode != NULL) {
for (size_t i = 0; i < pDeserializedInfo->pReasonCode->reasonCodeLength; i++) {
uint8_t code = pDeserializedInfo->pReasonCode->reasonCode[i];
if (code == MQTT_REASON_SUBACK_GRANTED_QOS0 ||
code == MQTT_REASON_SUBACK_GRANTED_QOS1 ||
code == MQTT_REASON_SUBACK_GRANTED_QOS2) {
// Subscription successful
} else {
// Subscription failed - check specific reason code
// e.g., MQTT_REASON_SUBACK_NOT_AUTHORIZED
}
}
}
}In v5.0, the server can send DISCONNECT packets. Handle them in your event callback:
if (pPacketInfo->type == MQTT_PACKET_TYPE_DISCONNECT) {
// Server initiated disconnect
if (pReasonCode != NULL) {
// Check reason code to understand why
switch (*pReasonCode) {
case MQTT_REASON_DISCONNECT_SERVER_SHUTTING_DOWN:
// Server is shutting down
break;
case MQTT_REASON_DISCONNECT_KEEP_ALIVE_TIMEOUT:
// Keep-alive timeout
break;
// Handle other reasons...
}
}
}MQTTSubscribeInfo_t subscription = {
.qos = MQTTQoS1,
.pTopicFilter = "device/+/telemetry",
.topicFilterLength = strlen("device/+/telemetry"),
// Don't receive messages published by this client
.noLocalOption = true,
// Preserve the retain flag from original publish
.retainAsPublishedOption = true,
// Only send retained messages if subscription doesn't exist
.retainHandlingOption = retainSendOnSubIfNotPresent
};Reduce bandwidth by using topic aliases for frequently published topics:
// First publish with full topic and alias
MQTTPropBuilder_t pubProps;
uint8_t pubPropsBuf[100];
MQTTPropertyBuilder_Init(&pubProps, pubPropsBuf, sizeof(pubPropsBuf));
uint16_t topicAlias = 1;
MQTTPropAdd_TopicAlias(&pubProps, topicAlias,
&(uint8_t){MQTT_PACKET_TYPE_PUBLISH});
MQTTPublishInfo_t publishInfo = {
.qos = MQTTQoS1,
.pTopicName = "device/sensor001/temperature",
.topicNameLength = strlen("device/sensor001/temperature"),
.pPayload = "25.5",
.payloadLength = 4
};
MQTT_Publish(&context, &publishInfo, packetId, &pubProps);
// Subsequent publishes can use empty topic with same alias
publishInfo.pTopicName = "";
publishInfo.topicNameLength = 0;
MQTT_Publish(&context, &publishInfo, nextPacketId, &pubProps);// Publisher sets response topic
MQTTPropBuilder_t pubProps;
uint8_t pubPropsBuf[100];
MQTTPropertyBuilder_Init(&pubProps, pubPropsBuf, sizeof(pubPropsBuf));
MQTTPropAdd_ResponseTopic(&pubProps, "response/topic",
strlen("response/topic"),
&(uint8_t){MQTT_PACKET_TYPE_PUBLISH});
MQTTPropAdd_CorrelationData(&pubProps, "req-123", strlen("req-123"),
&(uint8_t){MQTT_PACKET_TYPE_PUBLISH});
MQTT_Publish(&context, &publishInfo, packetId, &pubProps);
// Responder reads response topic and correlation data from incoming publish
// and uses them to send response// Before (coreMQTT v2.3.1)
void eventCallback(MQTTContext_t *pContext,
MQTTPacketInfo_t *pPacketInfo,
MQTTDeserializedInfo_t *pDeserializedInfo) {
// Handle events
}
MQTT_Connect(&context, &connectInfo, NULL, 1000, &sessionPresent);
MQTT_Subscribe(&context, subscriptions, count, packetId);
MQTT_Publish(&context, &publishInfo, packetId);
MQTT_Disconnect(&context);
// After (coreMQTT v5.0.0)
bool eventCallback(MQTTContext_t *pContext,
MQTTPacketInfo_t *pPacketInfo,
MQTTDeserializedInfo_t *pDeserializedInfo,
MQTTSuccessFailReasonCode_t *pReasonCode,
MQTTPropBuilder_t *pSendPropsBuffer,
MQTTPropBuilder_t *pGetPropsBuffer) {
// Handle events (same logic)
return true;
}
MQTT_Connect(&context, &connectInfo, NULL, 1000, &sessionPresent, NULL, NULL);
MQTT_Subscribe(&context, subscriptions, count, packetId, NULL);
MQTT_Publish(&context, &publishInfo, packetId, NULL);
MQTT_Disconnect(&context, NULL, NULL);// Initialize property builder
MQTTPropBuilder_t connectProps;
uint8_t connectPropsBuf[200];
MQTTPropertyBuilder_Init(&connectProps, connectPropsBuf, sizeof(connectPropsBuf));
// Set session expiry to 1 hour
uint32_t sessionExpiry = 3600;
MQTTPropAdd_SessionExpiry(&connectProps, sessionExpiry,
&(uint8_t){MQTT_PACKET_TYPE_CONNECT});
// Connect with session expiry
MQTT_Connect(&context, &connectInfo, NULL, 1000, &sessionPresent,
&connectProps, NULL);// Add custom metadata to publishes
MQTTPropBuilder_t pubProps;
uint8_t pubPropsBuf[300];
MQTTPropertyBuilder_Init(&pubProps, pubPropsBuf, sizeof(pubPropsBuf));
MQTTUserProperty_t deviceId = {
.pKey = "device-id",
.keyLength = strlen("device-id"),
.pValue = "sensor-001",
.valueLength = strlen("sensor-001")
};
MQTTPropAdd_UserProp(&pubProps, &deviceId,
&(uint8_t){MQTT_PACKET_TYPE_PUBLISH});
MQTTUserProperty_t timestamp = {
.pKey = "timestamp",
.keyLength = strlen("timestamp"),
.pValue = "2024-01-23T10:30:00Z",
.valueLength = strlen("2024-01-23T10:30:00Z")
};
MQTTPropAdd_UserProp(&pubProps, ×tamp,
&(uint8_t){MQTT_PACKET_TYPE_PUBLISH});
MQTT_Publish(&context, &publishInfo, packetId, &pubProps);MQTTPropBuilder_t disconnectProps;
uint8_t disconnectPropsBuf[200];
MQTTPropertyBuilder_Init(&disconnectProps, disconnectPropsBuf,
sizeof(disconnectPropsBuf));
MQTTPropAdd_ReasonString(&disconnectProps, "Shutting down for maintenance",
strlen("Shutting down for maintenance"),
&(uint8_t){MQTT_PACKET_TYPE_DISCONNECT});
MQTTSuccessFailReasonCode_t reason = MQTT_REASON_DISCONNECT_NORMAL_DISCONNECTION;
MQTT_Disconnect(&context, &disconnectProps, &reason);Solution: You're likely calling v5.0.0 APIs with v2.3.1 signatures. Add the additional parameters (use NULL if not using properties).
Solution: Check that your event callback returns bool and returns true. If it returns false, the library treats it as an error.
Solution: Ensure you:
- Initialize the property builder with
MQTTPropertyBuilder_Init() - Add properties with the correct packet type parameter
- Pass the property builder (not
NULL) to the API function
Solution: Increase the size of your property buffer. The required size depends on how many properties you're adding. Start with 500 bytes and adjust as needed.
Solution: Ensure your broker supports MQTT v5.0. Check the CONNACK properties to see what features the broker supports:
retainAvailableisWildcardAvailableisSharedAvailableisSubscriptionIdAvailable
Solution: Reason codes are only present in v5.0 brokers. If connecting to a v3.1.1 broker, reason codes will be NULL.
- MQTT v5.0 Specification: OASIS MQTT Version 5.0
- coreMQTT Documentation: Check the
docs/directory in the repository - Example Code: See
main.cin the repository root for a complete v5.0.0 example
- Update event callback signature to return
booland accept new parameters - Add
NULLparameters to all API calls if not using v5.0 features - Update
MQTT_InitStatefulQoSto include ACK properties buffer - Update
MQTTSubscribeInfo_tstructures with new subscription options - Test with your MQTT broker (ensure it supports v5.0 if using v5.0 features)
- Handle new reason codes in event callback
- Handle server-initiated DISCONNECT packets
- Consider using v5.0 features like properties, topic aliases, and enhanced subscriptions
Note: This migration guide assumes you're familiar with the coreMQTT v2.3.1 API. If you're new to coreMQTT, refer to the main documentation and examples in the repository.