├── Makefile.mqtt-service ├── README.md ├── example.c ├── mqtt-msg.c ├── mqtt-msg.h ├── mqtt-service.c └── mqtt-service.h /Makefile.mqtt-service: -------------------------------------------------------------------------------- 1 | mqtt-service_src = mqtt-msg.c mqtt-service.c 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MQTT client library for Contiki 2 | =============================== 3 | 4 | This is an MQTT client library for the Contiki operating system. 5 | 6 | It operates asynchronously, creating a new process to handle communication 7 | with the message broker. It supports subscribing, publishing, authentication, 8 | will messages, keep alive pings and all 3 QoS levels. In short, it should be 9 | a fully functional client, though some areas haven't been well tested yet. 10 | 11 | To use this library, create a mqtt-service directory in the Contiki apps 12 | directory and place these library files within it. Then add mqtt-service to 13 | the APPS variable in your application's makefile. 14 | 15 | See mqtt-service.h for documentation and example.c for example usage. 16 | 17 | For more exmples of usage, look in the devices/* directories in 18 | http://github.com/esar/myha 19 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | #include "mqtt-service.h" 2 | 3 | PROCESS_THREAD(mqtt_client_process, ev, data) 4 | { 5 | static uip_ip6addr_t server_address; 6 | 7 | // Allocate buffer space for the MQTT client 8 | static uint8_t in_buffer[64]; 9 | static uint8_t out_buffer[64]; 10 | 11 | // Setup an mqtt_connect_info_t structure to describe 12 | // how the connection should behave 13 | static mqtt_connect_info_t connect_info = 14 | { 15 | .client_id = "contiki", 16 | .username = NULL, 17 | .password = NULL, 18 | .will_topic = NULL, 19 | .will_message = NULL, 20 | .keepalive = 60, 21 | .will_qos = 0, 22 | .will_retain = 0, 23 | .clean_session = 1 24 | }; 25 | 26 | // The list of topics that we want to subscribe to 27 | static const char* topics[] = 28 | { 29 | "0", "1", "2", "3", "4", "5", NULL 30 | }; 31 | 32 | PROCESS_BEGIN(); 33 | 34 | // Set the server address 35 | uip_ip6addr(&server_address, 36 | 0xbbbb, 0, 0, 0, 0, 0, 0, 0x110); 37 | 38 | // Initialise the MQTT client 39 | mqtt_init(in_buffer, sizeof(in_buffer), 40 | out_buffer, sizeof(out_buffer)); 41 | 42 | // Ask the client to connect to the server 43 | // and wait for it to complete. 44 | mqtt_connect(&server_address, UIP_HTONS(1883), 45 | 1, &connect_info); 46 | PROCESS_WAIT_EVENT_UNTIL(ev == mqtt_event); 47 | 48 | if(mqtt_connected()) 49 | { 50 | static int i; 51 | 52 | for(i = 0; topics[i] != NULL; ++i) 53 | { 54 | // Ask the client to subscribe to the topic 55 | // and wait for it to complete 56 | mqtt_subscribe(topics[i]); 57 | PROCESS_WAIT_EVENT_UNTIL(ev == mqtt_event); 58 | } 59 | 60 | // Loop waiting for events from the MQTT client 61 | while(1) 62 | { 63 | PROCESS_WAIT_EVENT_UNTIL(ev == mqtt_event); 64 | 65 | // If we've received a publish event then print 66 | // out the topic and message 67 | if(mqtt_event_is_publish(data)) 68 | { 69 | const char* topic = mqtt_svc_event_get_topic(data); 70 | const char* message = mqtt_svc_event_get_message(data); 71 | int level = 0; 72 | 73 | printf("%s = %s\n", topic, message); 74 | 75 | // Relay the received message to a new topic 76 | mqtt_publish("new_topic", message, 0, 1); 77 | } 78 | } 79 | } 80 | else 81 | printf("mqtt service connect failed\n"); 82 | 83 | PROCESS_END(); 84 | } 85 | -------------------------------------------------------------------------------- /mqtt-msg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Stephen Robinson 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the copyright holder nor the names of its 15 | * contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | */ 31 | 32 | #include 33 | #include "mqtt-msg.h" 34 | 35 | #define MQTT_MAX_FIXED_HEADER_SIZE 3 36 | 37 | enum mqtt_connect_flag 38 | { 39 | MQTT_CONNECT_FLAG_USERNAME = 1 << 7, 40 | MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, 41 | MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, 42 | MQTT_CONNECT_FLAG_WILL = 1 << 2, 43 | MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 44 | }; 45 | 46 | struct __attribute((__packed__)) mqtt_connect_variable_header 47 | { 48 | uint8_t lengthMsb; 49 | uint8_t lengthLsb; 50 | uint8_t magic[6]; 51 | uint8_t version; 52 | uint8_t flags; 53 | uint8_t keepaliveMsb; 54 | uint8_t keepaliveLsb; 55 | }; 56 | 57 | static int append_string(mqtt_connection_t* connection, const char* string, int len) 58 | { 59 | if(connection->message.length + len + 2 > connection->buffer_length) 60 | return -1; 61 | 62 | connection->buffer[connection->message.length++] = len >> 8; 63 | connection->buffer[connection->message.length++] = len & 0xff; 64 | memcpy(connection->buffer + connection->message.length, string, len); 65 | connection->message.length += len; 66 | 67 | return len + 2; 68 | } 69 | 70 | static uint16_t append_message_id(mqtt_connection_t* connection, uint16_t message_id) 71 | { 72 | // If message_id is zero then we should assign one, otherwise 73 | // we'll use the one supplied by the caller 74 | while(message_id == 0) 75 | message_id = ++connection->message_id; 76 | 77 | if(connection->message.length + 2 > connection->buffer_length) 78 | return 0; 79 | 80 | connection->buffer[connection->message.length++] = message_id >> 8; 81 | connection->buffer[connection->message.length++] = message_id & 0xff; 82 | 83 | return message_id; 84 | } 85 | 86 | static int init_message(mqtt_connection_t* connection) 87 | { 88 | connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; 89 | return MQTT_MAX_FIXED_HEADER_SIZE; 90 | } 91 | 92 | static mqtt_message_t* fail_message(mqtt_connection_t* connection) 93 | { 94 | connection->message.data = connection->buffer; 95 | connection->message.length = 0; 96 | return &connection->message; 97 | } 98 | 99 | static mqtt_message_t* fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) 100 | { 101 | int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; 102 | 103 | if(remaining_length > 127) 104 | { 105 | connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); 106 | connection->buffer[1] = 0x80 | (remaining_length % 128); 107 | connection->buffer[2] = remaining_length / 128; 108 | connection->message.length = remaining_length + 3; 109 | connection->message.data = connection->buffer; 110 | } 111 | else 112 | { 113 | connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); 114 | connection->buffer[2] = remaining_length; 115 | connection->message.length = remaining_length + 2; 116 | connection->message.data = connection->buffer + 1; 117 | } 118 | 119 | return &connection->message; 120 | } 121 | 122 | void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) 123 | { 124 | memset(connection, 0, sizeof(mqtt_connection_t)); 125 | connection->buffer = buffer; 126 | connection->buffer_length = buffer_length; 127 | } 128 | 129 | int mqtt_get_total_length(uint8_t* buffer, uint16_t length) 130 | { 131 | int i; 132 | int totlen = 0; 133 | 134 | for(i = 1; i < length; ++i) 135 | { 136 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 137 | if((buffer[i] & 0x80) == 0) 138 | { 139 | ++i; 140 | break; 141 | } 142 | } 143 | totlen += i; 144 | 145 | return totlen; 146 | } 147 | 148 | const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) 149 | { 150 | int i; 151 | int totlen = 0; 152 | int topiclen; 153 | 154 | for(i = 1; i < *length; ++i) 155 | { 156 | totlen += (buffer[i] & 0x7f) << (7 * (i -1)); 157 | if((buffer[i] & 0x80) == 0) 158 | { 159 | ++i; 160 | break; 161 | } 162 | } 163 | totlen += i; 164 | 165 | if(i + 2 >= *length) 166 | return NULL; 167 | topiclen = buffer[i++] << 8; 168 | topiclen |= buffer[i++]; 169 | 170 | if(i + topiclen > *length) 171 | return NULL; 172 | 173 | *length = topiclen; 174 | return (const char*)(buffer + i); 175 | } 176 | 177 | const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) 178 | { 179 | int i; 180 | int totlen = 0; 181 | int topiclen; 182 | int blength = *length; 183 | *length = 0; 184 | 185 | for(i = 1; i < blength; ++i) 186 | { 187 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 188 | if((buffer[i] & 0x80) == 0) 189 | { 190 | ++i; 191 | break; 192 | } 193 | } 194 | totlen += i; 195 | 196 | if(i + 2 >= blength) 197 | return NULL; 198 | topiclen = buffer[i++] << 8; 199 | topiclen |= buffer[i++]; 200 | 201 | if(i + topiclen >= blength) 202 | return NULL; 203 | 204 | i += topiclen; 205 | 206 | if(mqtt_get_qos(buffer) > 0) 207 | { 208 | if(i + 2 >= blength) 209 | return NULL; 210 | i += 2; 211 | } 212 | 213 | if(totlen < i) 214 | return NULL; 215 | 216 | if(totlen <= blength) 217 | *length = totlen - i; 218 | else 219 | *length = blength - i; 220 | return (const char*)(buffer + i); 221 | } 222 | 223 | uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length) 224 | { 225 | if(length < 1) 226 | return 0; 227 | 228 | switch(mqtt_get_type(buffer)) 229 | { 230 | case MQTT_MSG_TYPE_PUBLISH: 231 | { 232 | int i; 233 | int topiclen; 234 | 235 | for(i = 1; i < length; ++i) 236 | { 237 | if((buffer[i] & 0x80) == 0) 238 | { 239 | ++i; 240 | break; 241 | } 242 | } 243 | 244 | if(i + 2 >= length) 245 | return 0; 246 | topiclen = buffer[i++] << 8; 247 | topiclen |= buffer[i++]; 248 | 249 | if(i + topiclen >= length) 250 | return 0; 251 | i += topiclen; 252 | 253 | if(mqtt_get_qos(buffer) > 0) 254 | { 255 | if(i + 2 >= length) 256 | return 0; 257 | } 258 | else 259 | return 0; 260 | 261 | return (buffer[i] << 8) | buffer[i + 1]; 262 | } 263 | case MQTT_MSG_TYPE_PUBACK: 264 | case MQTT_MSG_TYPE_PUBREC: 265 | case MQTT_MSG_TYPE_PUBREL: 266 | case MQTT_MSG_TYPE_PUBCOMP: 267 | case MQTT_MSG_TYPE_SUBACK: 268 | case MQTT_MSG_TYPE_UNSUBACK: 269 | case MQTT_MSG_TYPE_SUBSCRIBE: 270 | { 271 | // This requires the remaining length to be encoded in 1 byte, 272 | // which it should be. 273 | if(length >= 4 && (buffer[1] & 0x80) == 0) 274 | return (buffer[2] << 8) | buffer[3]; 275 | else 276 | return 0; 277 | } 278 | default: 279 | return 0; 280 | } 281 | } 282 | 283 | mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) 284 | { 285 | struct mqtt_connect_variable_header* variable_header; 286 | 287 | init_message(connection); 288 | 289 | if(connection->message.length + sizeof(*variable_header) > connection->buffer_length) 290 | return fail_message(connection); 291 | variable_header = (void*)(connection->buffer + connection->message.length); 292 | connection->message.length += sizeof(*variable_header); 293 | 294 | variable_header->lengthMsb = 0; 295 | variable_header->lengthLsb = 6; 296 | memcpy(variable_header->magic, "MQIsdp", 6); 297 | variable_header->version = 3; 298 | variable_header->flags = 0; 299 | variable_header->keepaliveMsb = info->keepalive >> 8; 300 | variable_header->keepaliveLsb = info->keepalive & 0xff; 301 | 302 | if(info->clean_session) 303 | variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; 304 | 305 | if(info->client_id != NULL && info->client_id[0] != '\0') 306 | { 307 | if(append_string(connection, info->client_id, strlen(info->client_id)) < 0) 308 | return fail_message(connection); 309 | } 310 | else 311 | return fail_message(connection); 312 | 313 | if(info->will_topic != NULL && info->will_topic[0] != '\0') 314 | { 315 | if(append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) 316 | return fail_message(connection); 317 | 318 | if(append_string(connection, info->will_message, strlen(info->will_message)) < 0) 319 | return fail_message(connection); 320 | 321 | variable_header->flags |= MQTT_CONNECT_FLAG_WILL; 322 | if(info->will_retain) 323 | variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; 324 | variable_header->flags |= (info->will_qos & 3) << 4; 325 | } 326 | 327 | if(info->username != NULL && info->username[0] != '\0') 328 | { 329 | if(append_string(connection, info->username, strlen(info->username)) < 0) 330 | return fail_message(connection); 331 | 332 | variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; 333 | } 334 | 335 | if(info->password != NULL && info->password[0] != '\0') 336 | { 337 | if(append_string(connection, info->password, strlen(info->password)) < 0) 338 | return fail_message(connection); 339 | 340 | variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; 341 | } 342 | 343 | return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); 344 | } 345 | 346 | mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) 347 | { 348 | init_message(connection); 349 | 350 | if(topic == NULL || topic[0] == '\0') 351 | return fail_message(connection); 352 | 353 | if(append_string(connection, topic, strlen(topic)) < 0) 354 | return fail_message(connection); 355 | 356 | if(qos > 0) 357 | { 358 | if((*message_id = append_message_id(connection, 0)) == 0) 359 | return fail_message(connection); 360 | } 361 | else 362 | *message_id = 0; 363 | 364 | if(connection->message.length + data_length > connection->buffer_length) 365 | return fail_message(connection); 366 | memcpy(connection->buffer + connection->message.length, data, data_length); 367 | connection->message.length += data_length; 368 | 369 | return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); 370 | } 371 | 372 | mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) 373 | { 374 | init_message(connection); 375 | if(append_message_id(connection, message_id) == 0) 376 | return fail_message(connection); 377 | return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); 378 | } 379 | 380 | mqtt_message_t* mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) 381 | { 382 | init_message(connection); 383 | if(append_message_id(connection, message_id) == 0) 384 | return fail_message(connection); 385 | return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); 386 | } 387 | 388 | mqtt_message_t* mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) 389 | { 390 | init_message(connection); 391 | if(append_message_id(connection, message_id) == 0) 392 | return fail_message(connection); 393 | return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); 394 | } 395 | 396 | mqtt_message_t* mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) 397 | { 398 | init_message(connection); 399 | if(append_message_id(connection, message_id) == 0) 400 | return fail_message(connection); 401 | return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); 402 | } 403 | 404 | mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) 405 | { 406 | init_message(connection); 407 | 408 | if(topic == NULL || topic[0] == '\0') 409 | return fail_message(connection); 410 | 411 | if((*message_id = append_message_id(connection, 0)) == 0) 412 | return fail_message(connection); 413 | 414 | if(append_string(connection, topic, strlen(topic)) < 0) 415 | return fail_message(connection); 416 | 417 | if(connection->message.length + 1 > connection->buffer_length) 418 | return fail_message(connection); 419 | connection->buffer[connection->message.length++] = qos; 420 | 421 | return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); 422 | } 423 | 424 | mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) 425 | { 426 | init_message(connection); 427 | 428 | if(topic == NULL || topic[0] == '\0') 429 | return fail_message(connection); 430 | 431 | if((*message_id = append_message_id(connection, 0)) == 0) 432 | return fail_message(connection); 433 | 434 | if(append_string(connection, topic, strlen(topic)) < 0) 435 | return fail_message(connection); 436 | 437 | return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); 438 | } 439 | 440 | mqtt_message_t* mqtt_msg_pingreq(mqtt_connection_t* connection) 441 | { 442 | init_message(connection); 443 | return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); 444 | } 445 | 446 | mqtt_message_t* mqtt_msg_pingresp(mqtt_connection_t* connection) 447 | { 448 | init_message(connection); 449 | return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); 450 | } 451 | 452 | mqtt_message_t* mqtt_msg_disconnect(mqtt_connection_t* connection) 453 | { 454 | init_message(connection); 455 | return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); 456 | } 457 | 458 | -------------------------------------------------------------------------------- /mqtt-msg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Stephen Robinson 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the copyright holder nor the names of its 15 | * contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | */ 31 | 32 | #include 33 | 34 | #ifndef MQTT_MSG_H_ 35 | #define MQTT_MSG_H_ 36 | 37 | enum mqtt_message_type 38 | { 39 | MQTT_MSG_TYPE_CONNECT = 1, 40 | MQTT_MSG_TYPE_CONNACK = 2, 41 | MQTT_MSG_TYPE_PUBLISH = 3, 42 | MQTT_MSG_TYPE_PUBACK = 4, 43 | MQTT_MSG_TYPE_PUBREC = 5, 44 | MQTT_MSG_TYPE_PUBREL = 6, 45 | MQTT_MSG_TYPE_PUBCOMP = 7, 46 | MQTT_MSG_TYPE_SUBSCRIBE = 8, 47 | MQTT_MSG_TYPE_SUBACK = 9, 48 | MQTT_MSG_TYPE_UNSUBSCRIBE = 10, 49 | MQTT_MSG_TYPE_UNSUBACK = 11, 50 | MQTT_MSG_TYPE_PINGREQ = 12, 51 | MQTT_MSG_TYPE_PINGRESP = 13, 52 | MQTT_MSG_TYPE_DISCONNECT = 14 53 | }; 54 | 55 | typedef struct 56 | { 57 | uint8_t* data; 58 | uint16_t length; 59 | 60 | } mqtt_message_t; 61 | 62 | typedef struct 63 | { 64 | mqtt_message_t message; 65 | 66 | uint16_t message_id; 67 | uint8_t* buffer; 68 | uint16_t buffer_length; 69 | 70 | } mqtt_connection_t; 71 | 72 | typedef struct mqtt_connect_info 73 | { 74 | const char* client_id; 75 | const char* username; 76 | const char* password; 77 | const char* will_topic; 78 | const char* will_message; 79 | int keepalive; 80 | int will_qos; 81 | int will_retain; 82 | int clean_session; 83 | 84 | } mqtt_connect_info_t; 85 | 86 | 87 | static inline int mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } 88 | static inline int mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } 89 | static inline int mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } 90 | static inline int mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } 91 | 92 | void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); 93 | int mqtt_get_total_length(uint8_t* buffer, uint16_t length); 94 | const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); 95 | const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); 96 | uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length); 97 | 98 | mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); 99 | mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); 100 | mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); 101 | mqtt_message_t* mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); 102 | mqtt_message_t* mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); 103 | mqtt_message_t* mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); 104 | mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); 105 | mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); 106 | mqtt_message_t* mqtt_msg_pingreq(mqtt_connection_t* connection); 107 | mqtt_message_t* mqtt_msg_pingresp(mqtt_connection_t* connection); 108 | mqtt_message_t* mqtt_msg_disconnect(mqtt_connection_t* connection); 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /mqtt-service.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Stephen Robinson 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the copyright holder nor the names of its 15 | * contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | */ 31 | 32 | #include "contiki.h" 33 | #include "contiki-net.h" 34 | #include "sys/etimer.h" 35 | 36 | #include 37 | #include 38 | 39 | #include "mqtt-service.h" 40 | 41 | 42 | typedef struct mqtt_state_t 43 | { 44 | uip_ip6addr_t address; 45 | uint16_t port; 46 | int auto_reconnect; 47 | mqtt_connect_info_t* connect_info; 48 | 49 | struct process* calling_process; 50 | struct uip_conn* tcp_connection; 51 | 52 | struct psock ps; 53 | uint8_t* in_buffer; 54 | uint8_t* out_buffer; 55 | int in_buffer_length; 56 | int out_buffer_length; 57 | uint16_t message_length; 58 | uint16_t message_length_read; 59 | mqtt_message_t* outbound_message; 60 | mqtt_connection_t mqtt_connection; 61 | uint16_t pending_msg_id; 62 | int pending_msg_type; 63 | 64 | } mqtt_state_t; 65 | 66 | PROCESS(mqtt_process, "MQTT Process"); 67 | 68 | process_event_t mqtt_event; 69 | mqtt_state_t mqtt_state; 70 | int mqtt_flags = 0; 71 | 72 | 73 | /********************************************************************* 74 | * 75 | * Public API 76 | * 77 | *********************************************************************/ 78 | 79 | // Initialise the MQTT client, must be called before anything else 80 | void mqtt_init(uint8_t* in_buffer, int in_buffer_length, uint8_t* out_buffer, int out_buffer_length) 81 | { 82 | mqtt_event = process_alloc_event(); 83 | 84 | mqtt_state.in_buffer = in_buffer; 85 | mqtt_state.in_buffer_length = in_buffer_length; 86 | mqtt_state.out_buffer = out_buffer; 87 | mqtt_state.out_buffer_length = out_buffer_length; 88 | } 89 | 90 | // Connect to the specified server 91 | int mqtt_connect(uip_ip6addr_t* address, uint16_t port, int auto_reconnect, mqtt_connect_info_t* info) 92 | { 93 | if(process_is_running(&mqtt_process)) 94 | return -1; 95 | 96 | mqtt_state.address = *address; 97 | mqtt_state.port = port; 98 | mqtt_state.auto_reconnect = auto_reconnect; 99 | mqtt_state.connect_info = info; 100 | mqtt_state.calling_process = PROCESS_CURRENT(); 101 | process_start(&mqtt_process, (void*)&mqtt_state); 102 | 103 | return 0; 104 | } 105 | 106 | // Disconnect from the server 107 | int mqtt_disconnect() 108 | { 109 | if(!process_is_running(&mqtt_process)) 110 | return -1; 111 | 112 | printf("mqtt: exiting...\n"); 113 | mqtt_flags &= ~MQTT_FLAG_READY; 114 | mqtt_flags |= MQTT_FLAG_EXIT; 115 | tcpip_poll_tcp(mqtt_state.tcp_connection); 116 | 117 | return 0; 118 | } 119 | 120 | // Subscribe to the specified topic 121 | int mqtt_subscribe(const char* topic) 122 | { 123 | if(!mqtt_ready()) 124 | return -1; 125 | 126 | printf("mqtt: sending subscribe...\n"); 127 | mqtt_state.outbound_message = mqtt_msg_subscribe(&mqtt_state.mqtt_connection, 128 | topic, 0, 129 | &mqtt_state.pending_msg_id); 130 | mqtt_flags &= ~MQTT_FLAG_READY; 131 | mqtt_state.pending_msg_type = MQTT_MSG_TYPE_SUBSCRIBE; 132 | tcpip_poll_tcp(mqtt_state.tcp_connection); 133 | 134 | return 0; 135 | } 136 | 137 | int mqtt_unsubscribe(const char* topic) 138 | { 139 | if(!mqtt_ready()) 140 | return -1; 141 | 142 | printf("sending unsubscribe\n"); 143 | mqtt_state.outbound_message = mqtt_msg_unsubscribe(&mqtt_state.mqtt_connection, topic, 144 | &mqtt_state.pending_msg_id); 145 | mqtt_flags &= ~MQTT_FLAG_READY; 146 | mqtt_state.pending_msg_type = MQTT_MSG_TYPE_UNSUBSCRIBE; 147 | tcpip_poll_tcp(mqtt_state.tcp_connection); 148 | 149 | return 0; 150 | } 151 | 152 | // Publish the specified message 153 | int mqtt_publish_with_length(const char* topic, const char* data, int data_length, int qos, int retain) 154 | { 155 | if(!mqtt_ready()) 156 | return -1; 157 | 158 | printf("mqtt: sending publish...\n"); 159 | mqtt_state.outbound_message = mqtt_msg_publish(&mqtt_state.mqtt_connection, 160 | topic, data, data_length, 161 | qos, retain, 162 | &mqtt_state.pending_msg_id); 163 | mqtt_flags &= ~MQTT_FLAG_READY; 164 | mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PUBLISH; 165 | tcpip_poll_tcp(mqtt_state.tcp_connection); 166 | 167 | return 0; 168 | } 169 | 170 | 171 | /*************************************************************** 172 | * 173 | * Internals 174 | * 175 | ***************************************************************/ 176 | 177 | static void complete_pending(mqtt_state_t* state, int event_type) 178 | { 179 | mqtt_event_data_t event_data; 180 | 181 | state->pending_msg_type = 0; 182 | mqtt_flags |= MQTT_FLAG_READY; 183 | event_data.type = event_type; 184 | process_post_synch(state->calling_process, mqtt_event, &event_data); 185 | } 186 | 187 | static void deliver_publish(mqtt_state_t* state, uint8_t* message, int length) 188 | { 189 | mqtt_event_data_t event_data; 190 | 191 | event_data.type = MQTT_EVENT_TYPE_PUBLISH; 192 | 193 | event_data.topic_length = length; 194 | event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); 195 | 196 | event_data.data_length = length; 197 | event_data.data = mqtt_get_publish_data(message, &event_data.data_length); 198 | 199 | memmove((char*)event_data.data + 1, (char*)event_data.data, event_data.data_length); 200 | event_data.data += 1; 201 | ((char*)event_data.topic)[event_data.topic_length] = '\0'; 202 | ((char*)event_data.data)[event_data.data_length] = '\0'; 203 | 204 | process_post_synch(state->calling_process, mqtt_event, &event_data); 205 | } 206 | 207 | static void deliver_publish_continuation(mqtt_state_t* state, uint16_t offset, uint8_t* buffer, uint16_t length) 208 | { 209 | mqtt_event_data_t event_data; 210 | 211 | event_data.type = MQTT_EVENT_TYPE_PUBLISH_CONTINUATION; 212 | event_data.topic_length = 0; 213 | event_data.topic = NULL; 214 | event_data.data_length = length; 215 | event_data.data_offset = offset; 216 | event_data.data = (char*)buffer; 217 | ((char*)event_data.data)[event_data.data_length] = '\0'; 218 | 219 | process_post_synch(state->calling_process, mqtt_event, &event_data); 220 | } 221 | 222 | static PT_THREAD(handle_mqtt_connection(mqtt_state_t* state)) 223 | { 224 | static struct etimer keepalive_timer; 225 | 226 | uint8_t msg_type; 227 | uint8_t msg_qos; 228 | uint16_t msg_id; 229 | 230 | PSOCK_BEGIN(&state->ps); 231 | 232 | // Initialise and send CONNECT message 233 | mqtt_msg_init(&state->mqtt_connection, state->out_buffer, state->out_buffer_length); 234 | state->outbound_message = mqtt_msg_connect(&state->mqtt_connection, state->connect_info); 235 | PSOCK_SEND(&state->ps, state->outbound_message->data, state->outbound_message->length); 236 | state->outbound_message = NULL; 237 | 238 | // Wait for CONACK message 239 | PSOCK_READBUF_LEN(&state->ps, 2); 240 | if(mqtt_get_type(state->in_buffer) != MQTT_MSG_TYPE_CONNACK) 241 | PSOCK_CLOSE_EXIT(&state->ps); 242 | 243 | // Tell the client we're connected 244 | mqtt_flags |= MQTT_FLAG_CONNECTED; 245 | complete_pending(state, MQTT_EVENT_TYPE_CONNECTED); 246 | 247 | // Setup the keep alive timer and enter main message processing loop 248 | etimer_set(&keepalive_timer, CLOCK_SECOND * state->connect_info->keepalive); 249 | while(1) 250 | { 251 | // Wait for something to happen: 252 | // new incoming data, 253 | // new outgoing data, 254 | // keep alive timer expired 255 | PSOCK_WAIT_UNTIL(&state->ps, PSOCK_NEWDATA(&state->ps) || 256 | state->outbound_message != NULL || 257 | etimer_expired(&keepalive_timer)); 258 | 259 | // If there's a new message waiting to go out, then send it 260 | if(state->outbound_message != NULL) 261 | { 262 | PSOCK_SEND(&state->ps, state->outbound_message->data, state->outbound_message->length); 263 | state->outbound_message = NULL; 264 | 265 | // If it was a PUBLISH message with QoS-0 then tell the client it's done 266 | if(state->pending_msg_type == MQTT_MSG_TYPE_PUBLISH && state->pending_msg_id == 0) 267 | complete_pending(state, MQTT_EVENT_TYPE_PUBLISHED); 268 | 269 | // Reset the keepalive timer as we've just sent some data 270 | etimer_restart(&keepalive_timer); 271 | continue; 272 | } 273 | 274 | // If the keep-alive timer expired then prepare a ping for sending 275 | // and reset the timer 276 | if(etimer_expired(&keepalive_timer)) 277 | { 278 | state->outbound_message = mqtt_msg_pingreq(&state->mqtt_connection); 279 | etimer_reset(&keepalive_timer); 280 | continue; 281 | } 282 | 283 | // If we get here we must have woken for new incoming data, 284 | // read and process it. 285 | PSOCK_READBUF_LEN(&state->ps, 2); 286 | 287 | state->message_length_read = PSOCK_DATALEN(&state->ps); 288 | state->message_length = mqtt_get_total_length(state->in_buffer, state->message_length_read); 289 | 290 | msg_type = mqtt_get_type(state->in_buffer); 291 | msg_qos = mqtt_get_qos(state->in_buffer); 292 | msg_id = mqtt_get_id(state->in_buffer, state->in_buffer_length); 293 | switch(msg_type) 294 | { 295 | case MQTT_MSG_TYPE_SUBACK: 296 | if(state->pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && state->pending_msg_id == msg_id) 297 | complete_pending(state, MQTT_EVENT_TYPE_SUBSCRIBED); 298 | break; 299 | case MQTT_MSG_TYPE_UNSUBACK: 300 | if(state->pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && state->pending_msg_id == msg_id) 301 | complete_pending(state, MQTT_EVENT_TYPE_UNSUBSCRIBED); 302 | break; 303 | case MQTT_MSG_TYPE_PUBLISH: 304 | if(msg_qos == 1) 305 | state->outbound_message = mqtt_msg_puback(&state->mqtt_connection, msg_id); 306 | else if(msg_qos == 2) 307 | state->outbound_message = mqtt_msg_pubrec(&state->mqtt_connection, msg_id); 308 | 309 | deliver_publish(state, state->in_buffer, state->message_length_read); 310 | break; 311 | case MQTT_MSG_TYPE_PUBACK: 312 | if(state->pending_msg_type == MQTT_MSG_TYPE_PUBLISH && state->pending_msg_id == msg_id) 313 | complete_pending(state, MQTT_EVENT_TYPE_PUBLISHED); 314 | break; 315 | case MQTT_MSG_TYPE_PUBREC: 316 | state->outbound_message = mqtt_msg_pubrel(&state->mqtt_connection, msg_id); 317 | break; 318 | case MQTT_MSG_TYPE_PUBREL: 319 | state->outbound_message = mqtt_msg_pubcomp(&state->mqtt_connection, msg_id); 320 | break; 321 | case MQTT_MSG_TYPE_PUBCOMP: 322 | if(state->pending_msg_type == MQTT_MSG_TYPE_PUBLISH && state->pending_msg_id == msg_id) 323 | complete_pending(state, MQTT_EVENT_TYPE_PUBLISHED); 324 | break; 325 | case MQTT_MSG_TYPE_PINGREQ: 326 | state->outbound_message = mqtt_msg_pingresp(&state->mqtt_connection); 327 | break; 328 | case MQTT_MSG_TYPE_PINGRESP: 329 | // Ignore 330 | break; 331 | } 332 | 333 | // NOTE: this is done down here and not in the switch case above 334 | // because the PSOCK_READBUF_LEN() won't work inside a switch 335 | // statement due to the way protothreads resume. 336 | if(msg_type == MQTT_MSG_TYPE_PUBLISH) 337 | { 338 | uint16_t len; 339 | 340 | // adjust message_length and message_length_read so that 341 | // they only account for the publish data and not the rest of the 342 | // message, this is done so that the offset passed with the 343 | // continuation event is the offset within the publish data and 344 | // not the offset within the message as a whole. 345 | len = state->message_length_read; 346 | mqtt_get_publish_data(state->in_buffer, &len); 347 | len = state->message_length_read - len; 348 | state->message_length -= len; 349 | state->message_length_read -= len; 350 | 351 | while(state->message_length_read < state->message_length) 352 | { 353 | PSOCK_READBUF_LEN(&state->ps, state->message_length - state->message_length_read); 354 | deliver_publish_continuation(state, state->message_length_read, state->in_buffer, PSOCK_DATALEN(&state->ps)); 355 | state->message_length_read += PSOCK_DATALEN(&state->ps); 356 | } 357 | } 358 | } 359 | 360 | PSOCK_END(&state->ps); 361 | } 362 | 363 | PROCESS_THREAD(mqtt_process, ev, data) 364 | { 365 | mqtt_event_data_t event_data; 366 | 367 | PROCESS_BEGIN(); 368 | 369 | while(1) 370 | { 371 | printf("mqtt: connecting...\n"); 372 | mqtt_state.tcp_connection = tcp_connect(&mqtt_state.address, 373 | mqtt_state.port, NULL); 374 | PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event); 375 | 376 | if(!uip_connected()) 377 | { 378 | printf("mqtt: connect failed\n"); 379 | continue; 380 | } 381 | else 382 | printf("mqtt: connected\n"); 383 | 384 | // reserve one byte at the end of the buffer so there's space to NULL terminate 385 | PSOCK_INIT(&mqtt_state.ps, mqtt_state.in_buffer, mqtt_state.in_buffer_length - 1); 386 | 387 | handle_mqtt_connection(&mqtt_state); 388 | 389 | while(1) 390 | { 391 | PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event); 392 | 393 | if(mqtt_flags & MQTT_FLAG_EXIT) 394 | { 395 | uip_close(); 396 | 397 | event_data.type = MQTT_EVENT_TYPE_EXITED; 398 | process_post_synch(mqtt_state.calling_process, mqtt_event, &event_data); 399 | PROCESS_EXIT(); 400 | } 401 | 402 | if(uip_aborted() || uip_timedout() || uip_closed()) 403 | { 404 | event_data.type = MQTT_EVENT_TYPE_DISCONNECTED; 405 | process_post_synch(mqtt_state.calling_process, mqtt_event, &event_data); 406 | printf("mqtt: lost connection: %s\n", uip_aborted() ? "aborted" : 407 | uip_timedout() ? "timed out" : 408 | uip_closed() ? "closed" : 409 | "unknown"); 410 | break; 411 | } 412 | else 413 | handle_mqtt_connection(&mqtt_state); 414 | } 415 | 416 | if(!mqtt_state.auto_reconnect) 417 | break; 418 | } 419 | 420 | event_data.type = MQTT_EVENT_TYPE_EXITED; 421 | process_post_synch(mqtt_state.calling_process, mqtt_event, &event_data); 422 | 423 | PROCESS_END(); 424 | } 425 | -------------------------------------------------------------------------------- /mqtt-service.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Stephen Robinson 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the copyright holder nor the names of its 15 | * contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | */ 31 | 32 | #ifndef MQTT_SERVICE_H_ 33 | #define MQTT_SERVICE_H_ 34 | 35 | #include 36 | #include "uip.h" 37 | #include "mqtt-msg.h" 38 | 39 | #define MQTT_FLAG_CONNECTED 1 40 | #define MQTT_FLAG_READY 2 41 | #define MQTT_FLAG_EXIT 4 42 | 43 | #define MQTT_EVENT_TYPE_NONE 0 44 | #define MQTT_EVENT_TYPE_CONNECTED 1 45 | #define MQTT_EVENT_TYPE_DISCONNECTED 2 46 | #define MQTT_EVENT_TYPE_SUBSCRIBED 3 47 | #define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 48 | #define MQTT_EVENT_TYPE_PUBLISH 5 49 | #define MQTT_EVENT_TYPE_PUBLISHED 6 50 | #define MQTT_EVENT_TYPE_EXITED 7 51 | #define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 52 | 53 | typedef struct mqtt_event_data_t 54 | { 55 | uint8_t type; 56 | const char* topic; 57 | const char* data; 58 | uint16_t topic_length; 59 | uint16_t data_length; 60 | uint16_t data_offset; 61 | 62 | } mqtt_event_data_t; 63 | 64 | extern int mqtt_flags; 65 | extern process_event_t mqtt_event; 66 | 67 | 68 | // Must be called before any other function is called. 69 | // 70 | // Initialises the MQTT client library and associates the 71 | // provided buffers with it. The buffer memory must remain 72 | // valid throughout the use of the API. 73 | void mqtt_init(uint8_t* in_buffer, int in_buffer_length, 74 | uint8_t* out_buffer, int out_buffer_length); 75 | 76 | // Starts an asynchronous connect to the server specified by 77 | // the address and port. If auto_reconnect is non-zero then 78 | // the it will keep trying to connect indefinitely and if the 79 | // connection drops it will attempt to reconnect. 80 | // The info structure provides other connection details 81 | // such as username/password, will topic/message, etc. 82 | // The memory pointed to by info must remain valid 83 | // throughout the use of the API. 84 | // 85 | // The calling process will receive an mqtt_event of type 86 | // MQTT_EVENT_CONNECTED when the operation is complete. 87 | // Or an event of type MQTT_EVENT_DISCONNECTED if the 88 | // connect attempt fails. 89 | int mqtt_connect(uip_ip6addr_t* address, uint16_t port, 90 | int auto_reconnect, mqtt_connect_info_t* info); 91 | 92 | // Starts an asynchronous disconnect from the server. 93 | // The calling process will receive a mqtt_event of type 94 | // MQTT_EVENT_TYPE_EXITED when the operation is complete. 95 | int mqtt_disconnect(); 96 | 97 | // Starts an asynchronous subscribe to the specified topic 98 | // The calling process will receive a mqtt_event of type 99 | // MQTT_EVENT_TYPE_SUBSCRIBE when the servers reply has 100 | // been received. 101 | int mqtt_subscribe(const char* topic); 102 | 103 | // Starts an asynchronous unsubscribe of the specified topic. 104 | // The calling process will receive a mqtt_event of type 105 | // MQTT_EVENT_TYPE_UNSUBSCRIBED when the server's reply 106 | // has been received. 107 | int mqtt_unsubscribe(const char* topic); 108 | 109 | // Same as mqtt_publish() but the data doesn't have to be 110 | // NULL terminated as a length is supplied instead. 111 | int mqtt_publish_with_length(const char* topic, const char* data, int data_length, int qos, int retain); 112 | 113 | // Starts an asynchronous publish of the specified data to 114 | // the specified topic. 115 | // The calling process will receive a mqtt_event of type 116 | // MQTT_EVENT_TYPE_PUBLISHED when the operation is complete 117 | static inline int mqtt_publish(const char* topic, const char* data, int qos, int retain) 118 | { 119 | return mqtt_publish_with_length(topic, data, data != NULL ? strlen(data) : 0, qos, retain); 120 | } 121 | 122 | 123 | static inline int mqtt_connected() 124 | { 125 | return (mqtt_flags & MQTT_FLAG_CONNECTED); 126 | } 127 | static inline int mqtt_ready() 128 | { 129 | return (mqtt_flags & MQTT_FLAG_READY); 130 | } 131 | 132 | static inline int mqtt_event_is_connected(void* data) 133 | { 134 | return ((mqtt_event_data_t*)data)->type == MQTT_EVENT_TYPE_CONNECTED; 135 | } 136 | static inline int mqtt_event_is_disconnected(void* data) 137 | { 138 | return ((mqtt_event_data_t*)data)->type == MQTT_EVENT_TYPE_DISCONNECTED; 139 | } 140 | static inline int mqtt_event_is_subscribed(void* data) 141 | { 142 | return ((mqtt_event_data_t*)data)->type == MQTT_EVENT_TYPE_SUBSCRIBED; 143 | } 144 | static inline int mqtt_event_is_unsubscribed(void* data) 145 | { 146 | return ((mqtt_event_data_t*)data)->type == MQTT_EVENT_TYPE_UNSUBSCRIBED; 147 | } 148 | static inline int mqtt_event_is_publish(void* data) 149 | { 150 | return ((mqtt_event_data_t*)data)->type == MQTT_EVENT_TYPE_PUBLISH; 151 | } 152 | static inline int mqtt_event_is_published(void* data) 153 | { 154 | return ((mqtt_event_data_t*)data)->type == MQTT_EVENT_TYPE_PUBLISHED; 155 | } 156 | static inline int mqtt_event_is_exited(void* data) 157 | { 158 | return ((mqtt_event_data_t*)data)->type == MQTT_EVENT_TYPE_EXITED; 159 | } 160 | static inline int mqtt_event_is_publish_continuation(void* data) 161 | { 162 | return ((mqtt_event_data_t*)data)->type == MQTT_EVENT_TYPE_PUBLISH_CONTINUATION; 163 | } 164 | 165 | static inline const char* mqtt_event_get_topic(void* data) 166 | { 167 | return ((mqtt_event_data_t*)data)->topic; 168 | } 169 | static inline uint16_t mqtt_event_get_topic_length(void* data) 170 | { 171 | return ((mqtt_event_data_t*)data)->topic_length; 172 | } 173 | static inline const char* mqtt_event_get_data(void* data) 174 | { 175 | return ((mqtt_event_data_t*)data)->data; 176 | } 177 | static inline uint16_t mqtt_event_get_data_length(void* data) 178 | { 179 | return ((mqtt_event_data_t*)data)->data_length; 180 | } 181 | static inline uint16_t mqtt_event_get_data_offset(void* data) 182 | { 183 | return ((mqtt_event_data_t*)data)->data_offset; 184 | } 185 | 186 | #endif 187 | --------------------------------------------------------------------------------