Skip to content

Latest commit

 

History

History
234 lines (200 loc) · 9.88 KB

MigrationGuide.md

File metadata and controls

234 lines (200 loc) · 9.88 KB

coreMQTT version >=v2.0.0 Migration Guide

With coreMQTT versions >=v2.0.0, there are some breaking changes that need to be addressed when upgrading.

Breaking Changes

  • The MQTT_ProcessLoop function no longer uses a timeout as this led to unavoidable busy-waiting. Thus, the signature of MQTT_ProcessLoop changed from MQTTStatus_t MQTT_ProcessLoop( MQTTContext_t * pContext, uint32_t timeoutMs ) to MQTTStatus_t MQTT_ProcessLoop( MQTTContext_t * pContext ). Additionally, MQTT_ProcessLoop can now return MQTTNeedMoreBytes. A return of MQTTNeedMoreBytes means that MQTT_ProcessLoop received only a part of an MQTT packet and will need to be called again (probably after a bit of delay) in order to finish receiving the MQTT packet. Thus, to migrate, simply remove the timeout from the MQTT_ProcessLoop call and additionally check for if MQTTNeedMoreBytes has been returned when checking the status of MQTT_ProcessLoop. For example:

Old Code Snippet:

// Variables used in this example.
MQTTStatus_t status;
uint32_t timeoutMs = 100;
// This context is assumed to be initialized and connected.
MQTTContext_t * pContext;

while( true )
{
     status = MQTT_ProcessLoop( pContext, timeoutMs );

     if( status != MQTTSuccess )
     {
         // Determine the error. It's possible we might need to disconnect
         // the underlying transport connection.
     }
     else
     {
         // Other application functions.
     }
}

New Code Snippet:

// Variables used in this example.
MQTTStatus_t status;
// This context is assumed to be initialized and connected.
MQTTContext_t * pContext;

while( true )
{
     status = MQTT_ProcessLoop( pContext );

     if( status != MQTTSuccess && status != MQTTNeedMoreBytes )
     {
         // Determine the error. It's possible we might need to disconnect
         // the underlying transport connection.
     }
     /* else if only required if the terminating else is empty and the application doesn't want a scenario akin to busy waiting. */
     else if( status == MQTTNeedMoreBytes )
     {
         /* Only a partial MQTT packet is received. Call MQTT_ProcessLoop again; ideally after a small delay. */
     }
     else
     {
         // Other application functions.
     }
}
  • The MQTT_ReceiveLoop function no longer uses a timeout as this led to unavoidable busy-waiting. Thus, the signature of MQTT_ReceiveLoop changed from MQTTStatus_t MQTT_ReceiveLoop( MQTTContext_t * pContext, uint32_t timeoutMs ) to MQTTStatus_t MQTT_ReceiveLoop( MQTTContext_t * pContext ). Additionally, MQTT_ReceiveLoop can now return MQTTNeedMoreBytes. A return of MQTTNeedMoreBytes means that MQTT_ReceiveLoop received only a part of an MQTT packet and will need to be called again (probably after a bit of delay) in order to finish receiving the MQTT packet. Thus, to migrate, simply remove the timeout from the MQTT_ReceiveLoop call and additionally check for if MQTTNeedMoreBytes has been returned when checking the status of MQTT_ReceiveLoop. For example:

Old Code Snippet:

// Variables used in this example.
MQTTStatus_t status;
uint32_t timeoutMs = 100;
// This context is assumed to be initialized and connected.
MQTTContext_t * pContext;

while( true )
{
     status = MQTT_ReceiveLoop( pContext, timeoutMs );

     if( status != MQTTSuccess )
     {
         // Determine the error. It's possible we might need to disconnect
         // the underlying transport connection.
     }
     else
     {
         // Other application functions.
     }
}

New Code Snippet:

// Variables used in this example.
MQTTStatus_t status;
// This context is assumed to be initialized and connected.
MQTTContext_t * pContext;

while( true )
{
     status = MQTT_ReceiveLoop( pContext );

     if( status != MQTTSuccess && status != MQTTNeedMoreBytes )
     {
         // Determine the error. It's possible we might need to disconnect
         // the underlying transport connection.
     }
     /* else if only required if the terminating else is empty and the application doesn't want a scenario akin to busy waiting. */
     else if( status == MQTTNeedMoreBytes )
     {
         /* Only a partial MQTT packet is received. Call MQTT_ReceiveLoop again; ideally after a small delay. */
     }
     else
     {
         // Other application functions.
     }
}
  • The TransportInterface_t structure now has a new member writev. It uses scatter-gather approach to send multiple MQTT packet components as a single packet to reduce overhead and improve performance. However, it is COMPLETELY OPTIONAL to implement. To that end, when application(s) initialize a TransportInterface_t structure, they MUST either set writev to a working implementation or set it NULL. Not doing this will lead to undefined behavior as the coreMQTT library checks if writev is NULL to determine if it should be used. For example:

Old Code Snippet:

TransportInterface_t transport;
// Set transport interface members.
transport.pNetworkInterface = &someNetworkInterface;
transport.send = networkSend;
transport.recv = networkRecv;

New Code Snippet:

TransportInterface_t transport;
// Set transport interface members.
transport.pNetworkInterface = &someNetworkInterface;
transport.send = networkSend;
transport.recv = networkRecv;
transport.writev = NULL;
  • The MQTT_Init function no longer creates buffers to handle QoS > 0 packets, so if planning to use QoS > 0, the MQTT_InitStatefulQoS function must also be called on an MQTTContext_t after calling MQTT_Init on it and before using any other coreMQTT functions with it. If not using QoS > 0, MQTT_InitStatefulQoS does not need to be called. For example (code that uses QoS > 0):

Old Code Snippet:

// Function for obtaining a timestamp.
uint32_t getTimeStampMs();
// Callback function for receiving packets.
void eventCallback(
     MQTTContext_t * pContext,
     MQTTPacketInfo_t * pPacketInfo,
     MQTTDeserializedInfo_t * pDeserializedInfo
);
// Network send.
int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes );
// Network receive.
int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes );

MQTTContext_t mqttContext;
TransportInterface_t transport;
MQTTFixedBuffer_t fixedBuffer;
uint8_t buffer[ 1024 ];

// Clear context.
memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) );

// Set transport interface members.
transport.pNetworkInterface = &someNetworkInterface;
transport.send = networkSend;
transport.recv = networkRecv;

// Set buffer members.
fixedBuffer.pBuffer = buffer;
fixedBuffer.size = 1024;

status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer );

if( status == MQTTSuccess )
{
     // Do something with mqttContext. The transport and fixedBuffer structs were
     // copied into the context, so the original structs do not need to stay in scope.
}

New Code Snippet:

// Function for obtaining a timestamp.
uint32_t getTimeStampMs();
// Callback function for receiving packets.
void eventCallback(
     MQTTContext_t * pContext,
     MQTTPacketInfo_t * pPacketInfo,
     MQTTDeserializedInfo_t * pDeserializedInfo
);
// Network send.
int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes );
// Network receive.
int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes );

MQTTContext_t mqttContext;
TransportInterface_t transport;
MQTTFixedBuffer_t fixedBuffer;
uint8_t buffer[ 1024 ];
static MQTTPubAckInfo_t pOutgoingPublishRecords[ OUTGOING_PUBLISH_RECORD_COUNT ];
static MQTTPubAckInfo_t pIncomingPublishRecords[ INCOMING_PUBLISH_RECORD_COUNT ];

// Clear context.
memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) );

// Set transport interface members.
transport.pNetworkInterface = &someNetworkInterface;
transport.send = networkSend;
transport.recv = networkRecv;
transport.writev = NULL;

// Set buffer members.
fixedBuffer.pBuffer = buffer;
fixedBuffer.size = 1024;

status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer );

if( status == MQTTSuccess )
{
    // Initialize MQTT context for QoS > 0. This only has to be done if
    // performing QoS > 0 operations.
    status = MQTT_InitStatefulQoS(pxMQTTContext,
                                   pOutgoingPublishRecords,
                                   OUTGOING_PUBLISH_RECORD_COUNT,
                                   pIncomingPublishRecords,
                                   INCOMING_PUBLISH_RECORD_COUNT);
}

if( status == MQTTSuccess )
{
     // Do something with mqttContext. The transport and fixedBuffer structs were
     // copied into the context, so the original structs do not need to stay in scope.
}
  • For coreMQTT version >=v2.1.0, the MQTT_SEND_RETRY_TIMEOUT_MS macro configuration has been deprecated. It has been replaced with MQTT_SEND_TIMEOUT_MS. MQTT_SEND_RETRY_TIMEOUT_MS was formerly used to timeout sending an MQTT packet after the transport send function failed to transmit any bytes for too long. This timeout would reset each time the transport send function sent any bytes, leading to the actual timeout scaling with the number of bytes being sent. In other words, the maximum time for sending a packet was variable. In order to remedy this, the MQTT_SEND_RETRY_TIMEOUT_MS macro was replaced with MQTT_SEND_TIMEOUT_MS. MQTT_SEND_TIMEOUT_MS now sets the maximum duration that coreMQTT will attempt to send an MQTT packet, leading to more consistent timeout behavior across various APIs and packet lengths.

Additional Changes

  • The MQTT_CancelCallback function has been added to allow a program to prevent the event callback from being called when receiving an ACK for a sent packet. For example, if a program sends a publish with packet ID 2 and QoS > 0 using MQTT_Publish, the program could then call MQTT_CancelCallback on packet ID 2 to prevent coreMQTT from calling the event callback when it receives the PUBACK for packet ID 2.