|
| 1 | +## coreMQTT version >=v2.0.0 Migration Guide |
| 2 | + |
| 3 | +With coreMQTT versions >=v2.0.0, there are some breaking changes that need to be addressed when upgrading. |
| 4 | + |
| 5 | +### Breaking Changes |
| 6 | + |
| 7 | +* 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: |
| 8 | + |
| 9 | +**Old Code Snippet**: |
| 10 | +``` |
| 11 | +// Variables used in this example. |
| 12 | +MQTTStatus_t status; |
| 13 | +uint32_t timeoutMs = 100; |
| 14 | +// This context is assumed to be initialized and connected. |
| 15 | +MQTTContext_t * pContext; |
| 16 | +
|
| 17 | +while( true ) |
| 18 | +{ |
| 19 | + status = MQTT_ProcessLoop( pContext, timeoutMs ); |
| 20 | +
|
| 21 | + if( status != MQTTSuccess ) |
| 22 | + { |
| 23 | + // Determine the error. It's possible we might need to disconnect |
| 24 | + // the underlying transport connection. |
| 25 | + } |
| 26 | + else |
| 27 | + { |
| 28 | + // Other application functions. |
| 29 | + } |
| 30 | +} |
| 31 | +``` |
| 32 | +**New Code Snippet**: |
| 33 | +``` |
| 34 | +// Variables used in this example. |
| 35 | +MQTTStatus_t status; |
| 36 | +// This context is assumed to be initialized and connected. |
| 37 | +MQTTContext_t * pContext; |
| 38 | +
|
| 39 | +while( true ) |
| 40 | +{ |
| 41 | + status = MQTT_ProcessLoop( pContext ); |
| 42 | +
|
| 43 | + if( status != MQTTSuccess && status != MQTTNeedMoreBytes ) |
| 44 | + { |
| 45 | + // Determine the error. It's possible we might need to disconnect |
| 46 | + // the underlying transport connection. |
| 47 | + } |
| 48 | + /* else if only required if the terminating else is empty and the application doesn't want a scenario akin to busy waiting. */ |
| 49 | + else if( status == MQTTNeedMoreBytes ) |
| 50 | + { |
| 51 | + /* Only a partial MQTT packet is received. Call MQTT_ProcessLoop again; ideally after a small delay. */ |
| 52 | + } |
| 53 | + else |
| 54 | + { |
| 55 | + // Other application functions. |
| 56 | + } |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +* 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: |
| 61 | + |
| 62 | +**Old Code Snippet**: |
| 63 | +``` |
| 64 | +// Variables used in this example. |
| 65 | +MQTTStatus_t status; |
| 66 | +uint32_t timeoutMs = 100; |
| 67 | +// This context is assumed to be initialized and connected. |
| 68 | +MQTTContext_t * pContext; |
| 69 | +
|
| 70 | +while( true ) |
| 71 | +{ |
| 72 | + status = MQTT_ReceiveLoop( pContext, timeoutMs ); |
| 73 | +
|
| 74 | + if( status != MQTTSuccess ) |
| 75 | + { |
| 76 | + // Determine the error. It's possible we might need to disconnect |
| 77 | + // the underlying transport connection. |
| 78 | + } |
| 79 | + else |
| 80 | + { |
| 81 | + // Other application functions. |
| 82 | + } |
| 83 | +} |
| 84 | +``` |
| 85 | +**New Code Snippet**: |
| 86 | +``` |
| 87 | +// Variables used in this example. |
| 88 | +MQTTStatus_t status; |
| 89 | +// This context is assumed to be initialized and connected. |
| 90 | +MQTTContext_t * pContext; |
| 91 | +
|
| 92 | +while( true ) |
| 93 | +{ |
| 94 | + status = MQTT_ReceiveLoop( pContext ); |
| 95 | +
|
| 96 | + if( status != MQTTSuccess && status != MQTTNeedMoreBytes ) |
| 97 | + { |
| 98 | + // Determine the error. It's possible we might need to disconnect |
| 99 | + // the underlying transport connection. |
| 100 | + } |
| 101 | + /* else if only required if the terminating else is empty and the application doesn't want a scenario akin to busy waiting. */ |
| 102 | + else if( status == MQTTNeedMoreBytes ) |
| 103 | + { |
| 104 | + /* Only a partial MQTT packet is received. Call MQTT_ReceiveLoop again; ideally after a small delay. */ |
| 105 | + } |
| 106 | + else |
| 107 | + { |
| 108 | + // Other application functions. |
| 109 | + } |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +* 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: |
| 114 | + |
| 115 | +**Old Code Snippet**: |
| 116 | +``` |
| 117 | +TransportInterface_t transport; |
| 118 | +// Set transport interface members. |
| 119 | +transport.pNetworkInterface = &someNetworkInterface; |
| 120 | +transport.send = networkSend; |
| 121 | +transport.recv = networkRecv; |
| 122 | +``` |
| 123 | +**New Code Snippet**: |
| 124 | +``` |
| 125 | +TransportInterface_t transport; |
| 126 | +// Set transport interface members. |
| 127 | +transport.pNetworkInterface = &someNetworkInterface; |
| 128 | +transport.send = networkSend; |
| 129 | +transport.recv = networkRecv; |
| 130 | +transport.writev = NULL; |
| 131 | +``` |
| 132 | + |
| 133 | +* 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): |
| 134 | + |
| 135 | +**Old Code Snippet**: |
| 136 | +``` |
| 137 | +// Function for obtaining a timestamp. |
| 138 | +uint32_t getTimeStampMs(); |
| 139 | +// Callback function for receiving packets. |
| 140 | +void eventCallback( |
| 141 | + MQTTContext_t * pContext, |
| 142 | + MQTTPacketInfo_t * pPacketInfo, |
| 143 | + MQTTDeserializedInfo_t * pDeserializedInfo |
| 144 | +); |
| 145 | +// Network send. |
| 146 | +int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes ); |
| 147 | +// Network receive. |
| 148 | +int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes ); |
| 149 | +
|
| 150 | +MQTTContext_t mqttContext; |
| 151 | +TransportInterface_t transport; |
| 152 | +MQTTFixedBuffer_t fixedBuffer; |
| 153 | +uint8_t buffer[ 1024 ]; |
| 154 | +
|
| 155 | +// Clear context. |
| 156 | +memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) ); |
| 157 | +
|
| 158 | +// Set transport interface members. |
| 159 | +transport.pNetworkInterface = &someNetworkInterface; |
| 160 | +transport.send = networkSend; |
| 161 | +transport.recv = networkRecv; |
| 162 | +
|
| 163 | +// Set buffer members. |
| 164 | +fixedBuffer.pBuffer = buffer; |
| 165 | +fixedBuffer.size = 1024; |
| 166 | +
|
| 167 | +status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer ); |
| 168 | +
|
| 169 | +if( status == MQTTSuccess ) |
| 170 | +{ |
| 171 | + // Do something with mqttContext. The transport and fixedBuffer structs were |
| 172 | + // copied into the context, so the original structs do not need to stay in scope. |
| 173 | +} |
| 174 | +``` |
| 175 | +**New Code Snippet**: |
| 176 | +``` |
| 177 | +// Function for obtaining a timestamp. |
| 178 | +uint32_t getTimeStampMs(); |
| 179 | +// Callback function for receiving packets. |
| 180 | +void eventCallback( |
| 181 | + MQTTContext_t * pContext, |
| 182 | + MQTTPacketInfo_t * pPacketInfo, |
| 183 | + MQTTDeserializedInfo_t * pDeserializedInfo |
| 184 | +); |
| 185 | +// Network send. |
| 186 | +int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes ); |
| 187 | +// Network receive. |
| 188 | +int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes ); |
| 189 | +
|
| 190 | +MQTTContext_t mqttContext; |
| 191 | +TransportInterface_t transport; |
| 192 | +MQTTFixedBuffer_t fixedBuffer; |
| 193 | +uint8_t buffer[ 1024 ]; |
| 194 | +static MQTTPubAckInfo_t pOutgoingPublishRecords[ OUTGOING_PUBLISH_RECORD_COUNT ]; |
| 195 | +static MQTTPubAckInfo_t pIncomingPublishRecords[ INCOMING_PUBLISH_RECORD_COUNT ]; |
| 196 | +
|
| 197 | +// Clear context. |
| 198 | +memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) ); |
| 199 | +
|
| 200 | +// Set transport interface members. |
| 201 | +transport.pNetworkInterface = &someNetworkInterface; |
| 202 | +transport.send = networkSend; |
| 203 | +transport.recv = networkRecv; |
| 204 | +transport.writev = NULL; |
| 205 | +
|
| 206 | +// Set buffer members. |
| 207 | +fixedBuffer.pBuffer = buffer; |
| 208 | +fixedBuffer.size = 1024; |
| 209 | +
|
| 210 | +status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer ); |
| 211 | +
|
| 212 | +if( status == MQTTSuccess ) |
| 213 | +{ |
| 214 | + // Initialize MQTT context for QoS > 0. This only has to be done if |
| 215 | + // performing QoS > 0 operations. |
| 216 | + status = MQTT_InitStatefulQoS(pxMQTTContext, |
| 217 | + pOutgoingPublishRecords, |
| 218 | + OUTGOING_PUBLISH_RECORD_COUNT, |
| 219 | + pIncomingPublishRecords, |
| 220 | + INCOMING_PUBLISH_RECORD_COUNT); |
| 221 | +} |
| 222 | +
|
| 223 | +if( status == MQTTSuccess ) |
| 224 | +{ |
| 225 | + // Do something with mqttContext. The transport and fixedBuffer structs were |
| 226 | + // copied into the context, so the original structs do not need to stay in scope. |
| 227 | +} |
| 228 | +``` |
| 229 | + |
| 230 | +* 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. |
| 231 | + |
| 232 | +### Additional Changes |
| 233 | + |
| 234 | +* 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. |
0 commit comments