├── src ├── mqtt │ ├── utils.h │ ├── typedef.h │ ├── debug.h │ ├── mqtt_config.h │ ├── ringbuf.h │ ├── proto.h │ ├── ringbuf.c │ ├── queue.h │ ├── queue.c │ ├── proto.c │ ├── utils.c │ ├── mqtt_msg.h │ ├── mqtt.h │ ├── mqtt_msg.c │ └── mqtt.c ├── MQTT.h └── MQTT.cpp ├── .gitignore ├── README.md ├── library.properties ├── keywords.txt └── examples ├── mqtt_sub └── mqtt_sub.ino └── mqtt_pub └── mqtt_pub.ino /src/mqtt/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H_ 2 | #define _UTILS_H_ 3 | 4 | #include "c_types.h" 5 | 6 | uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s); 7 | uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip); 8 | uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str); 9 | #endif 10 | -------------------------------------------------------------------------------- /src/mqtt/typedef.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * Standard Types definition 4 | */ 5 | 6 | #ifndef _TYPE_DEF_H_ 7 | #define _TYPE_DEF_H_ 8 | 9 | typedef char I8; 10 | typedef unsigned char U8; 11 | typedef short I16; 12 | typedef unsigned short U16; 13 | typedef long I32; 14 | typedef unsigned long U32; 15 | typedef unsigned long long U64; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | #*.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | #Makefile 14 | *-build-* 15 | build-* 16 | *.autosave 17 | 18 | # .log files (usually created by QtTest - thanks to VestniK) 19 | *.log 20 | 21 | 22 | # Editors temporary files 23 | *~ 24 | 25 | 26 | #OSX 27 | .DS_Store 28 | ._* 29 | -------------------------------------------------------------------------------- /src/mqtt/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * debug.h 3 | * 4 | * Created on: Dec 4, 2014 5 | * Author: Minh 6 | */ 7 | 8 | #ifndef USER_DEBUG_H_ 9 | #define USER_DEBUG_H_ 10 | 11 | 12 | #if defined(MQTT_DEBUG_ON) 13 | #define MQTT_INFO( format, ... ) os_printf( format, ## __VA_ARGS__ ) 14 | #else 15 | #define MQTT_INFO( format, ... ) 16 | #endif 17 | 18 | 19 | #endif /* USER_DEBUG_H_ */ 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MQTT 2 | ==== 3 | 4 | A Wrapper around mqtt for Arduino to be used with esp8266 modules. 5 | 6 | It wraps a slightly modified version of mqtt for esp8266 ported by Tuan PM. 7 | Original code for esp: https://github.com/tuanpmt/esp_mqtt 8 | Original code for contiki: https://github.com/esar/contiki-mqtt 9 | 10 | 11 | ==== 12 | 13 | **secure libssl:** 14 | 15 | If you want to use secure communication, please use the `secure`-branch! 16 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP MQTT 2 | version=1.0.1 3 | author=Ingo Randolf 4 | maintainer=Ingo Randolf 5 | sentence=A Wrapper around mqtt for Arduino to be used with esp8266 modules. 6 | paragraph=It wraps a slightly modified version of mqtt for esp8266 ported by Tuan PM. Original code for esp: https://github.com/tuanpmt/esp_mqtt (7ec2ef8e1df0422b77348fe1da7885568e0c9d01) Original code for contiki: https://github.com/esar/contiki-mqtt 7 | category=Communication 8 | url=https://github.com/i-n-g-o/esp-mqtt-arduino 9 | architectures=esp8266 -------------------------------------------------------------------------------- /src/mqtt/mqtt_config.h: -------------------------------------------------------------------------------- 1 | #ifndef __MQTT_CONFIG_H__ 2 | #define __MQTT_CONFIG_H__ 3 | 4 | //#define MQTT_SSL_ENABLE 5 | 6 | /*DEFAULT CONFIGURATIONS*/ 7 | 8 | #define MQTT_BUF_SIZE 1024 9 | #define MQTT_KEEPALIVE 120 /*second*/ 10 | 11 | #define MQTT_RECONNECT_TIMEOUT 5 /*second*/ 12 | 13 | #define QUEUE_BUFFER_SIZE 2048 14 | 15 | #define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/ 16 | //PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/ 17 | 18 | #endif // __MQTT_CONFIG_H__ -------------------------------------------------------------------------------- /src/mqtt/ringbuf.h: -------------------------------------------------------------------------------- 1 | #ifndef _RING_BUF_H_ 2 | #define _RING_BUF_H_ 3 | 4 | #include 5 | #include 6 | #include "typedef.h" 7 | 8 | typedef struct { 9 | U8* p_o; /**< Original pointer */ 10 | U8* volatile p_r; /**< Read pointer */ 11 | U8* volatile p_w; /**< Write pointer */ 12 | volatile I32 fill_cnt; /**< Number of filled slots */ 13 | I32 size; /**< Buffer size */ 14 | } RINGBUF; 15 | 16 | I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size); 17 | I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c); 18 | I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c); 19 | #endif 20 | -------------------------------------------------------------------------------- /src/mqtt/proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: proto.h 3 | * Author: ThuHien 4 | * 5 | * Created on November 23, 2012, 8:57 AM 6 | */ 7 | 8 | #ifndef _PROTO_H_ 9 | #define _PROTO_H_ 10 | #include 11 | #include "typedef.h" 12 | #include "ringbuf.h" 13 | 14 | typedef void(PROTO_PARSE_CALLBACK)(); 15 | 16 | typedef struct { 17 | U8 *buf; 18 | U16 bufSize; 19 | U16 dataLen; 20 | U8 isEsc; 21 | U8 isBegin; 22 | PROTO_PARSE_CALLBACK* callback; 23 | } PROTO_PARSER; 24 | 25 | I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize); 26 | I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len); 27 | I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize); 28 | I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len); 29 | I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value); 30 | I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF *rb, U8 *bufOut, U16* len, U16 maxBufLen); 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Test 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | MQTT.h KEYWORD1 10 | 11 | MQTT KEYWORD1 12 | 13 | 14 | ####################################### 15 | # Methods and Functions (KEYWORD2) 16 | ####################################### 17 | 18 | setClientId KEYWORD2 19 | setUserPwd KEYWORD2 20 | 21 | connect KEYWORD2 22 | disconnect KEYWORD2 23 | isConnected KEYWORD2 24 | 25 | publish KEYWORD2 26 | subscribe KEYWORD2 27 | 28 | getState KEYWORD2 29 | 30 | #general 31 | onConnected KEYWORD2 32 | onDisconnected KEYWORD2 33 | onPublished KEYWORD2 34 | onData KEYWORD2 35 | 36 | ####################################### 37 | # Instances (KEYWORD2) 38 | ####################################### 39 | 40 | ####################################### 41 | # Constants (LITERAL1) 42 | ####################################### 43 | 44 | -------------------------------------------------------------------------------- /src/mqtt/ringbuf.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * Ring Buffer library 4 | */ 5 | 6 | #include "ringbuf.h" 7 | 8 | 9 | /** 10 | * \brief init a RINGBUF object 11 | * \param r pointer to a RINGBUF object 12 | * \param buf pointer to a byte array 13 | * \param size size of buf 14 | * \return 0 if successfull, otherwise failed 15 | */ 16 | I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size) 17 | { 18 | if (r == NULL || buf == NULL || size < 2) return -1; 19 | 20 | r->p_o = r->p_r = r->p_w = buf; 21 | r->fill_cnt = 0; 22 | r->size = size; 23 | 24 | return 0; 25 | } 26 | /** 27 | * \brief put a character into ring buffer 28 | * \param r pointer to a ringbuf object 29 | * \param c character to be put 30 | * \return 0 if successfull, otherwise failed 31 | */ 32 | I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c) 33 | { 34 | if (r->fill_cnt >= r->size)return -1; // ring buffer is full, this should be atomic operation 35 | 36 | 37 | r->fill_cnt++; // increase filled slots count, this should be atomic operation 38 | 39 | 40 | *r->p_w++ = c; // put character into buffer 41 | 42 | if (r->p_w >= r->p_o + r->size) // rollback if write pointer go pass 43 | r->p_w = r->p_o; // the physical boundary 44 | 45 | return 0; 46 | } 47 | /** 48 | * \brief get a character from ring buffer 49 | * \param r pointer to a ringbuf object 50 | * \param c read character 51 | * \return 0 if successfull, otherwise failed 52 | */ 53 | I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c) 54 | { 55 | if (r->fill_cnt <= 0)return -1; // ring buffer is empty, this should be atomic operation 56 | 57 | 58 | r->fill_cnt--; // decrease filled slots count 59 | 60 | 61 | *c = *r->p_r++; // get the character out 62 | 63 | if (r->p_r >= r->p_o + r->size) // rollback if write pointer go pass 64 | r->p_r = r->p_o; // the physical boundary 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /examples/mqtt_sub/mqtt_sub.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void myDataCb(String& topic, String& data); 5 | void myPublishedCb(); 6 | void myDisconnectedCb(); 7 | void myConnectedCb(); 8 | 9 | #define CLIENT_ID "client3" 10 | #define TOPIC "/client1/value" 11 | 12 | 13 | // create MQTT 14 | MQTT myMqtt(CLIENT_ID, "192.168.0.1", 1883); 15 | 16 | 17 | const char* ssid = "ssid"; 18 | const char* password = "ssid_password"; 19 | 20 | 21 | // 22 | void setup() { 23 | Serial.begin(115200); 24 | delay(1000); 25 | 26 | Serial.println(); 27 | Serial.println(); 28 | Serial.print("Connecting to "); 29 | Serial.println(ssid); 30 | 31 | WiFi.begin(ssid, password); 32 | 33 | while (WiFi.status() != WL_CONNECTED) { 34 | delay(500); 35 | Serial.print("."); 36 | } 37 | 38 | Serial.println(""); 39 | Serial.println("WiFi connected"); 40 | Serial.println("IP address: "); 41 | Serial.println(WiFi.localIP()); 42 | 43 | 44 | Serial.println("Connecting to MQTT server"); 45 | 46 | // setup callbacks 47 | myMqtt.onConnected(myConnectedCb); 48 | myMqtt.onDisconnected(myDisconnectedCb); 49 | myMqtt.onPublished(myPublishedCb); 50 | myMqtt.onData(myDataCb); 51 | 52 | Serial.println("connect mqtt..."); 53 | myMqtt.connect(); 54 | 55 | Serial.println("subscribe to topic..."); 56 | myMqtt.subscribe(TOPIC); 57 | 58 | delay(10); 59 | } 60 | 61 | // 62 | void loop() { 63 | } 64 | 65 | 66 | /* 67 | * 68 | */ 69 | void myConnectedCb() 70 | { 71 | Serial.println("connected to MQTT server"); 72 | } 73 | 74 | void myDisconnectedCb() 75 | { 76 | Serial.println("disconnected. try to reconnect..."); 77 | delay(500); 78 | myMqtt.connect(); 79 | } 80 | 81 | void myPublishedCb() 82 | { 83 | //Serial.println("published."); 84 | } 85 | 86 | void myDataCb(String& topic, String& data) 87 | { 88 | 89 | Serial.print(topic); 90 | Serial.print(": "); 91 | Serial.println(data); 92 | } 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/mqtt/queue.h: -------------------------------------------------------------------------------- 1 | /* str_queue.h -- 2 | * 3 | * Copyright (c) 2014-2015, Tuan PM 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * 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 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * 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 OWNER 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 | #ifndef USER_QUEUE_H_ 32 | #define USER_QUEUE_H_ 33 | #include "os_type.h" 34 | #include "ringbuf.h" 35 | typedef struct { 36 | uint8_t *buf; 37 | RINGBUF rb; 38 | } QUEUE; 39 | 40 | void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize); 41 | int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len); 42 | int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); 43 | bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue); 44 | #endif /* USER_QUEUE_H_ */ 45 | -------------------------------------------------------------------------------- /examples/mqtt_pub/mqtt_pub.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void myDataCb(String& topic, String& data); 5 | void myPublishedCb(); 6 | void myDisconnectedCb(); 7 | void myConnectedCb(); 8 | 9 | #define CLIENT_ID "client1" 10 | 11 | // create MQTT object 12 | MQTT myMqtt(CLIENT_ID, "192.168.0.1", 1883); 13 | 14 | // 15 | const char* ssid = "ssid"; 16 | const char* password = "ssid_password"; 17 | 18 | 19 | // 20 | void setup() { 21 | Serial.begin(115200); 22 | delay(1000); 23 | 24 | Serial.println(); 25 | Serial.println(); 26 | Serial.print("Connecting to "); 27 | Serial.println(ssid); 28 | 29 | WiFi.begin(ssid, password); 30 | 31 | while (WiFi.status() != WL_CONNECTED) { 32 | delay(500); 33 | Serial.print("."); 34 | } 35 | 36 | Serial.println(""); 37 | Serial.println("WiFi connected"); 38 | Serial.println("IP address: "); 39 | Serial.println(WiFi.localIP()); 40 | 41 | Serial.println("Connecting to MQTT server"); 42 | 43 | // setup callbacks 44 | myMqtt.onConnected(myConnectedCb); 45 | myMqtt.onDisconnected(myDisconnectedCb); 46 | myMqtt.onPublished(myPublishedCb); 47 | myMqtt.onData(myDataCb); 48 | 49 | Serial.println("connect mqtt..."); 50 | myMqtt.connect(); 51 | 52 | delay(10); 53 | } 54 | 55 | // 56 | void loop() { 57 | 58 | int value = analogRead(A0); 59 | 60 | String topic("/"); 61 | topic += CLIENT_ID; 62 | topic += "/value"; 63 | 64 | String valueStr(value); 65 | 66 | // publish value to topic 67 | boolean result = myMqtt.publish(topic, valueStr); 68 | 69 | delay(1000); 70 | } 71 | 72 | 73 | /* 74 | * 75 | */ 76 | void myConnectedCb() 77 | { 78 | Serial.println("connected to MQTT server"); 79 | } 80 | 81 | void myDisconnectedCb() 82 | { 83 | Serial.println("disconnected. try to reconnect..."); 84 | delay(500); 85 | myMqtt.connect(); 86 | } 87 | 88 | void myPublishedCb() 89 | { 90 | //Serial.println("published."); 91 | } 92 | 93 | void myDataCb(String& topic, String& data) 94 | { 95 | 96 | Serial.print(topic); 97 | Serial.print(": "); 98 | Serial.println(data); 99 | } 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/mqtt/queue.c: -------------------------------------------------------------------------------- 1 | /* str_queue.c 2 | * 3 | * Copyright (c) 2014-2015, Tuan PM 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * 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 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * 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 OWNER 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 | #include "queue.h" 31 | 32 | #include "user_interface.h" 33 | #include "osapi.h" 34 | #include "os_type.h" 35 | #include "mem.h" 36 | #include "proto.h" 37 | 38 | uint8_t *last_rb_p_r; 39 | uint8_t *last_rb_p_w; 40 | uint32_t last_fill_cnt; 41 | 42 | void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize) 43 | { 44 | queue->buf = (uint8_t*)os_zalloc(bufferSize); 45 | RINGBUF_Init(&queue->rb, queue->buf, bufferSize); 46 | } 47 | int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len) 48 | { 49 | uint32_t ret; 50 | 51 | last_rb_p_r = queue->rb.p_r; 52 | last_rb_p_w = queue->rb.p_w; 53 | last_fill_cnt = queue->rb.fill_cnt; 54 | 55 | ret = PROTO_AddRb(&queue->rb, buffer, len); 56 | if (ret == -1) { 57 | // rolling ring buffer back 58 | queue->rb.p_r = last_rb_p_r; 59 | queue->rb.p_w = last_rb_p_w; 60 | queue->rb.fill_cnt = last_fill_cnt; 61 | } 62 | return ret; 63 | } 64 | int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) 65 | { 66 | 67 | return PROTO_ParseRb(&queue->rb, buffer, len, maxLen); 68 | } 69 | 70 | BOOL ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue) 71 | { 72 | if (queue->rb.fill_cnt <= 0) 73 | return TRUE; 74 | return FALSE; 75 | } 76 | -------------------------------------------------------------------------------- /src/MQTT.h: -------------------------------------------------------------------------------- 1 | /*//------------------------------------------------------------------------------- 2 | * MQTT.h 3 | * 4 | * Header file for MQTT Wrapper 5 | * 6 | * Wrapper for Arduino written by Ingo Randolf during 7 | * eTextiles Summercamp 2015. 8 | * 9 | * This library is intended to be used with esp8266 modules. 10 | * 11 | * 12 | * This class wraps a slightly modified version of mqtt 13 | * for esp8266 written by Tuan PM. 14 | * Original code: https://github.com/tuanpmt/esp_mqtt 15 | * 16 | * 17 | * This library is free software; you can redistribute it and/or 18 | * modify it under the terms of the GNU Lesser General Public 19 | * License as published by the Free Software Foundation; either 20 | * version 2.1 of the License, or (at your option) any later version. 21 | * 22 | * This library is distributed in the hope that it will be useful, 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 | * Lesser General Public License for more details. 26 | //-------------------------------------------------------------------------------*/ 27 | #ifndef MQTT_WRAPPER_H 28 | #define MQTT_WRAPPER_H 29 | 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | extern "C" { 39 | #include 40 | #include "mqtt/mqtt.h" 41 | } 42 | 43 | 44 | class MQTT 45 | { 46 | public: 47 | MQTT(const char* client_id, const char* host, uint32_t port); 48 | ~MQTT(); 49 | 50 | void setClientId(const char* client_id); 51 | void setUserPwd(const char* user, const char* pwd); 52 | 53 | void connect(); 54 | void disconnect(); 55 | bool isConnected(); 56 | 57 | bool publish(const char* topic, const char* buf, uint32_t buf_len, int qos = 0, int retain = 0); 58 | bool publish(String& topic, String& data, int qos = 0, int retain = 0); 59 | bool publish(String& topic, const char* buf, uint32_t buf_len, int qos = 0, int retain = 0); 60 | bool publish(const char* topic, String& data, int qos = 0, int retain = 0); 61 | 62 | bool subscribe(const char* topic, uint8_t qos = 0); 63 | bool subscribe(const String& topic, uint8_t qos = 0); 64 | 65 | int getState() { return mqttClient.connState; }; 66 | 67 | // set callbacks 68 | void onConnected( void (*)(void) ); 69 | void onDisconnected( void (*)(void) ); 70 | void onPublished( void (*)(void) ); 71 | void onData( void (*)(String&, String&) ); 72 | void onData( void (*)(const char*, uint32_t, const char*, uint32_t) ); 73 | 74 | // user callbacks 75 | void (*onMqttConnectedCb)(void); 76 | void (*onMqttDisconnectedCb)(void); 77 | void (*onMqttPublishedCb)(void); 78 | void (*onMqttDataCb) (String&, String&); 79 | void (*onMqttDataRawCb) (const char*, uint32_t, const char*, uint32_t); 80 | 81 | // internal callback 82 | void _onMqttDataCb(const char*, uint32_t, const char*, uint32_t); 83 | 84 | private: 85 | MQTT_Client mqttClient; 86 | 87 | }; 88 | 89 | 90 | 91 | 92 | #endif -------------------------------------------------------------------------------- /src/mqtt/proto.c: -------------------------------------------------------------------------------- 1 | #include "proto.h" 2 | #include "ringbuf.h" 3 | I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize) 4 | { 5 | parser->buf = buf; 6 | parser->bufSize = bufSize; 7 | parser->dataLen = 0; 8 | parser->callback = completeCallback; 9 | parser->isEsc = 0; 10 | return 0; 11 | } 12 | 13 | I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value) 14 | { 15 | switch (value) { 16 | case 0x7D: 17 | parser->isEsc = 1; 18 | break; 19 | 20 | case 0x7E: 21 | parser->dataLen = 0; 22 | parser->isEsc = 0; 23 | parser->isBegin = 1; 24 | break; 25 | 26 | case 0x7F: 27 | if (parser->callback != NULL) 28 | parser->callback(); 29 | parser->isBegin = 0; 30 | return 0; 31 | break; 32 | 33 | default: 34 | if (parser->isBegin == 0) break; 35 | 36 | if (parser->isEsc) { 37 | value ^= 0x20; 38 | parser->isEsc = 0; 39 | } 40 | 41 | if (parser->dataLen < parser->bufSize) 42 | parser->buf[parser->dataLen++] = value; 43 | 44 | break; 45 | } 46 | return -1; 47 | } 48 | 49 | I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len) 50 | { 51 | while (len--) 52 | PROTO_ParseByte(parser, *buf++); 53 | 54 | return 0; 55 | } 56 | I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, U8 *bufOut, U16* len, U16 maxBufLen) 57 | { 58 | U8 c; 59 | 60 | PROTO_PARSER proto; 61 | PROTO_Init(&proto, NULL, bufOut, maxBufLen); 62 | while (RINGBUF_Get(rb, &c) == 0) { 63 | if (PROTO_ParseByte(&proto, c) == 0) { 64 | *len = proto.dataLen; 65 | return 0; 66 | } 67 | } 68 | return -1; 69 | } 70 | I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize) 71 | { 72 | U16 i = 2; 73 | U16 len = *(U16*) packet; 74 | 75 | if (bufSize < 1) return -1; 76 | 77 | *buf++ = 0x7E; 78 | bufSize--; 79 | 80 | while (len--) { 81 | switch (*packet) { 82 | case 0x7D: 83 | case 0x7E: 84 | case 0x7F: 85 | if (bufSize < 2) return -1; 86 | *buf++ = 0x7D; 87 | *buf++ = *packet++ ^ 0x20; 88 | i += 2; 89 | bufSize -= 2; 90 | break; 91 | default: 92 | if (bufSize < 1) return -1; 93 | *buf++ = *packet++; 94 | i++; 95 | bufSize--; 96 | break; 97 | } 98 | } 99 | 100 | if (bufSize < 1) return -1; 101 | *buf++ = 0x7F; 102 | 103 | return i; 104 | } 105 | 106 | I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len) 107 | { 108 | U16 i = 2; 109 | if (RINGBUF_Put(rb, 0x7E) == -1) return -1; 110 | while (len--) { 111 | switch (*packet) { 112 | case 0x7D: 113 | case 0x7E: 114 | case 0x7F: 115 | if (RINGBUF_Put(rb, 0x7D) == -1) return -1; 116 | if (RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; 117 | i += 2; 118 | break; 119 | default: 120 | if (RINGBUF_Put(rb, *packet++) == -1) return -1; 121 | i++; 122 | break; 123 | } 124 | } 125 | if (RINGBUF_Put(rb, 0x7F) == -1) return -1; 126 | 127 | return i; 128 | } 129 | 130 | -------------------------------------------------------------------------------- /src/mqtt/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Tuan PM 3 | * Email: tuanpm@live.com 4 | * 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | */ 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "utils.h" 39 | 40 | 41 | uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str) 42 | { 43 | uint8_t segs = 0; /* Segment count. */ 44 | uint8_t chcnt = 0; /* Character count within segment. */ 45 | uint8_t accum = 0; /* Accumulator for segment. */ 46 | /* Catch NULL pointer. */ 47 | if (str == 0) 48 | return 0; 49 | /* Process every character in string. */ 50 | 51 | while (*str != '\0') { 52 | /* Segment changeover. */ 53 | 54 | if (*str == '.') { 55 | /* Must have some digits in segment. */ 56 | if (chcnt == 0) 57 | return 0; 58 | /* Limit number of segments. */ 59 | if (++segs == 4) 60 | return 0; 61 | /* Reset segment values and restart loop. */ 62 | chcnt = accum = 0; 63 | str++; 64 | continue; 65 | } 66 | 67 | /* Check numeric. */ 68 | if ((*str < '0') || (*str > '9')) 69 | return 0; 70 | 71 | /* Accumulate and check segment. */ 72 | 73 | if ((accum = accum * 10 + *str - '0') > 255) 74 | return 0; 75 | /* Advance other segment specific stuff and continue loop. */ 76 | 77 | chcnt++; 78 | str++; 79 | } 80 | 81 | /* Check enough segments and enough characters in last segment. */ 82 | 83 | if (segs != 3) 84 | return 0; 85 | if (chcnt == 0) 86 | return 0; 87 | /* Address okay. */ 88 | 89 | return 1; 90 | } 91 | uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip) 92 | { 93 | 94 | /* The count of the number of bytes processed. */ 95 | int i; 96 | /* A pointer to the next digit to process. */ 97 | const char * start; 98 | 99 | start = str; 100 | for (i = 0; i < 4; i++) { 101 | /* The digit being processed. */ 102 | char c; 103 | /* The value of this byte. */ 104 | int n = 0; 105 | while (1) { 106 | c = * start; 107 | start++; 108 | if (c >= '0' && c <= '9') { 109 | n *= 10; 110 | n += c - '0'; 111 | } 112 | /* We insist on stopping at "." if we are still parsing 113 | the first, second, or third numbers. If we have reached 114 | the end of the numbers, we will allow any character. */ 115 | else if ((i < 3 && c == '.') || i == 3) { 116 | break; 117 | } 118 | else { 119 | return 0; 120 | } 121 | } 122 | if (n >= 256) { 123 | return 0; 124 | } 125 | ((uint8_t*)ip)[i] = n; 126 | } 127 | return 1; 128 | 129 | } 130 | uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s) 131 | { 132 | uint32_t value = 0, digit; 133 | int8_t c; 134 | 135 | while ((c = *s++)) { 136 | if ('0' <= c && c <= '9') 137 | digit = c - '0'; 138 | else if ('A' <= c && c <= 'F') 139 | digit = c - 'A' + 10; 140 | else if ('a' <= c && c <= 'f') 141 | digit = c - 'a' + 10; 142 | else break; 143 | 144 | value = (value << 4) | digit; 145 | } 146 | 147 | return value; 148 | } 149 | 150 | -------------------------------------------------------------------------------- /src/mqtt/mqtt_msg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: mqtt_msg.h 3 | * Author: Minh Tuan 4 | * 5 | * Created on July 12, 2014, 1:05 PM 6 | */ 7 | 8 | #ifndef MQTT_MSG_H 9 | #define MQTT_MSG_H 10 | #include "c_types.h" 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | /* 16 | * Copyright (c) 2014, Stephen Robinson 17 | * All rights reserved. 18 | * 19 | * Redistribution and use in source and binary forms, with or without 20 | * modification, are permitted provided that the following conditions 21 | * are met: 22 | * 23 | * 1. Redistributions of source code must retain the above copyright 24 | * notice, this list of conditions and the following disclaimer. 25 | * 2. Redistributions in binary form must reproduce the above copyright 26 | * notice, this list of conditions and the following disclaimer in the 27 | * documentation and/or other materials provided with the distribution. 28 | * 3. Neither the name of the copyright holder nor the names of its 29 | * contributors may be used to endorse or promote products derived 30 | * from this software without specific prior written permission. 31 | * 32 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 33 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 36 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 37 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 38 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 39 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 40 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 41 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 42 | * POSSIBILITY OF SUCH DAMAGE. 43 | * 44 | */ 45 | /* 7 6 5 4 3 2 1 0*/ 46 | /*| --- Message Type---- | DUP Flag | QoS Level | Retain | 47 | /* Remaining Length */ 48 | 49 | 50 | enum mqtt_message_type 51 | { 52 | MQTT_MSG_TYPE_CONNECT = 1, 53 | MQTT_MSG_TYPE_CONNACK = 2, 54 | MQTT_MSG_TYPE_PUBLISH = 3, 55 | MQTT_MSG_TYPE_PUBACK = 4, 56 | MQTT_MSG_TYPE_PUBREC = 5, 57 | MQTT_MSG_TYPE_PUBREL = 6, 58 | MQTT_MSG_TYPE_PUBCOMP = 7, 59 | MQTT_MSG_TYPE_SUBSCRIBE = 8, 60 | MQTT_MSG_TYPE_SUBACK = 9, 61 | MQTT_MSG_TYPE_UNSUBSCRIBE = 10, 62 | MQTT_MSG_TYPE_UNSUBACK = 11, 63 | MQTT_MSG_TYPE_PINGREQ = 12, 64 | MQTT_MSG_TYPE_PINGRESP = 13, 65 | MQTT_MSG_TYPE_DISCONNECT = 14 66 | }; 67 | 68 | enum mqtt_connect_return_code 69 | { 70 | CONNECTION_ACCEPTED = 0, 71 | CONNECTION_REFUSE_PROTOCOL, 72 | CONNECTION_REFUSE_ID_REJECTED, 73 | CONNECTION_REFUSE_SERVER_UNAVAILABLE, 74 | CONNECTION_REFUSE_BAD_USERNAME, 75 | CONNECTION_REFUSE_NOT_AUTHORIZED 76 | }; 77 | 78 | typedef struct mqtt_message 79 | { 80 | uint8_t* data; 81 | uint16_t length; 82 | 83 | } mqtt_message_t; 84 | 85 | typedef struct mqtt_connection 86 | { 87 | mqtt_message_t message; 88 | 89 | uint16_t message_id; 90 | uint8_t* buffer; 91 | uint16_t buffer_length; 92 | 93 | } mqtt_connection_t; 94 | 95 | typedef struct mqtt_connect_info 96 | { 97 | char* client_id; 98 | char* username; 99 | char* password; 100 | char* will_topic; 101 | char* will_message; 102 | uint32_t keepalive; 103 | int will_qos; 104 | int will_retain; 105 | int clean_session; 106 | 107 | } mqtt_connect_info_t; 108 | 109 | 110 | static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } 111 | static inline int ICACHE_FLASH_ATTR mqtt_get_connect_return_code(uint8_t* buffer) { return buffer[3]; } 112 | static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } 113 | static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } 114 | static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } 115 | 116 | void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); 117 | int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length); 118 | const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); 119 | const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); 120 | uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length); 121 | 122 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); 123 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); 124 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); 125 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); 126 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); 127 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); 128 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); 129 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); 130 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection); 131 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection); 132 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection); 133 | 134 | 135 | #ifdef __cplusplus 136 | } 137 | #endif 138 | 139 | #endif /* MQTT_MSG_H */ 140 | 141 | -------------------------------------------------------------------------------- /src/mqtt/mqtt.h: -------------------------------------------------------------------------------- 1 | /* mqtt.h 2 | * 3 | * Copyright (c) 2014-2015, Tuan PM 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * 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 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * 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 OWNER 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 | #ifndef USER_AT_MQTT_H_ 31 | #define USER_AT_MQTT_H_ 32 | #include "mqtt_config.h" 33 | #include "mqtt_msg.h" 34 | #include "user_interface.h" 35 | 36 | #include "queue.h" 37 | typedef struct mqtt_event_data_t 38 | { 39 | uint8_t type; 40 | const char* topic; 41 | const char* data; 42 | uint16_t topic_length; 43 | uint16_t data_length; 44 | uint16_t data_offset; 45 | } mqtt_event_data_t; 46 | 47 | typedef struct mqtt_state_t 48 | { 49 | uint16_t port; 50 | int auto_reconnect; 51 | mqtt_connect_info_t* connect_info; 52 | uint8_t* in_buffer; 53 | uint8_t* out_buffer; 54 | int in_buffer_length; 55 | int out_buffer_length; 56 | uint16_t message_length; 57 | uint16_t message_length_read; 58 | mqtt_message_t* outbound_message; 59 | mqtt_connection_t mqtt_connection; 60 | uint16_t pending_msg_id; 61 | int pending_msg_type; 62 | int pending_publish_qos; 63 | } mqtt_state_t; 64 | 65 | typedef enum { 66 | WIFI_INIT, 67 | WIFI_CONNECTING, 68 | WIFI_CONNECTING_ERROR, 69 | WIFI_CONNECTED, 70 | DNS_RESOLVE, 71 | TCP_DISCONNECTING, 72 | TCP_DISCONNECTED, 73 | TCP_RECONNECT_DISCONNECTING, 74 | TCP_RECONNECT_REQ, 75 | TCP_RECONNECT, 76 | TCP_CONNECTING, 77 | TCP_CONNECTING_ERROR, 78 | TCP_CONNECTED, 79 | MQTT_CONNECT_SEND, 80 | MQTT_CONNECT_SENDING, 81 | MQTT_SUBSCIBE_SEND, 82 | MQTT_SUBSCIBE_SENDING, 83 | MQTT_DATA, 84 | MQTT_KEEPALIVE_SEND, 85 | MQTT_PUBLISH_RECV, 86 | MQTT_PUBLISHING, 87 | MQTT_DELETING, 88 | MQTT_DELETED, 89 | } tConnState; 90 | 91 | typedef void (*MqttCallback)(uint32_t *args); 92 | typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh); 93 | 94 | typedef struct { 95 | struct espconn *pCon; 96 | uint8_t security; 97 | uint8_t* host; 98 | uint32_t port; 99 | ip_addr_t ip; 100 | mqtt_state_t mqtt_state; 101 | mqtt_connect_info_t connect_info; 102 | MqttCallback connectedCb; 103 | MqttCallback disconnectedCb; 104 | MqttCallback publishedCb; 105 | MqttCallback timeoutCb; 106 | MqttDataCallback dataCb; 107 | ETSTimer mqttTimer; 108 | uint32_t keepAliveTick; 109 | uint32_t reconnectTick; 110 | uint32_t sendTimeout; 111 | tConnState connState; 112 | QUEUE msgQueue; 113 | void* user_data; 114 | } MQTT_Client; 115 | 116 | #define SEC_NONSSL 0 117 | #define SEC_SSL 1 118 | 119 | #define MQTT_FLAG_CONNECTED 1 120 | #define MQTT_FLAG_READY 2 121 | #define MQTT_FLAG_EXIT 4 122 | 123 | #define MQTT_EVENT_TYPE_NONE 0 124 | #define MQTT_EVENT_TYPE_CONNECTED 1 125 | #define MQTT_EVENT_TYPE_DISCONNECTED 2 126 | #define MQTT_EVENT_TYPE_SUBSCRIBED 3 127 | #define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 128 | #define MQTT_EVENT_TYPE_PUBLISH 5 129 | #define MQTT_EVENT_TYPE_PUBLISHED 6 130 | #define MQTT_EVENT_TYPE_EXITED 7 131 | #define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 132 | 133 | void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security); 134 | bool ICACHE_FLASH_ATTR MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession); 135 | void ICACHE_FLASH_ATTR MQTT_DeleteClient(MQTT_Client *mqttClient); 136 | void ICACHE_FLASH_ATTR MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain); 137 | 138 | void ICACHE_FLASH_ATTR MQTT_SetUserId(MQTT_Client *mqttClient, const char* client_id); 139 | void ICACHE_FLASH_ATTR MQTT_SetUserPwd(MQTT_Client *mqttClient, const char* user_id, const char* pwd); 140 | 141 | void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb); 142 | void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb); 143 | void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb); 144 | void ICACHE_FLASH_ATTR MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb); 145 | void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb); 146 | bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos); 147 | bool ICACHE_FLASH_ATTR MQTT_UnSubscribe(MQTT_Client *client, char* topic); 148 | void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient); 149 | void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient); 150 | bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain); 151 | 152 | #endif /* USER_AT_MQTT_H_ */ 153 | -------------------------------------------------------------------------------- /src/MQTT.cpp: -------------------------------------------------------------------------------- 1 | /*//------------------------------------------------------------------------------- 2 | * MQTT.cpp 3 | * 4 | * Implementation file for MQTT Wrapper 5 | * 6 | * Wrapper for Arduino written by Ingo Randolf during 7 | * eTextiles Summercamp 2015. 8 | * 9 | * This library is intended to be used with esp8266 modules. 10 | * 11 | * 12 | * This class wraps a slightly modified version of mqtt 13 | * for esp8266 written by Tuan PM. 14 | * Original code: https://github.com/tuanpmt/esp_mqtt 15 | * 16 | * 17 | * This library is free software; you can redistribute it and/or 18 | * modify it under the terms of the GNU Lesser General Public 19 | * License as published by the Free Software Foundation; either 20 | * version 2.1 of the License, or (at your option) any later version. 21 | * 22 | * This library is distributed in the hope that it will be useful, 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 | * Lesser General Public License for more details. 26 | //-------------------------------------------------------------------------------*/ 27 | #include "MQTT.h" 28 | 29 | #include "user_interface.h" 30 | #include "osapi.h" 31 | #include "os_type.h" 32 | #include "mqtt/debug.h" 33 | 34 | 35 | //------------------------------------------------------------------------------------ 36 | // mqtt internal callbacks 37 | //------------------------------------------------------------------------------------ 38 | static void mqttConnectedCb(uint32_t *args) 39 | { 40 | MQTT_Client* client = (MQTT_Client*)args; 41 | 42 | MQTT* _this = (MQTT*)client->user_data; 43 | 44 | if (_this && _this->onMqttConnectedCb) { 45 | _this->onMqttConnectedCb(); 46 | } 47 | } 48 | 49 | static void mqttDisconnectedCb(uint32_t *args) 50 | { 51 | MQTT_Client* client = (MQTT_Client*)args; 52 | 53 | MQTT* _this = (MQTT*)client->user_data; 54 | 55 | if (_this && _this->onMqttDisconnectedCb) { 56 | _this->onMqttDisconnectedCb(); 57 | } 58 | } 59 | 60 | static void mqttPublishedCb(uint32_t *args) 61 | { 62 | MQTT_Client* client = (MQTT_Client*)args; 63 | 64 | MQTT* _this = (MQTT*)client->user_data; 65 | 66 | if (_this && _this->onMqttPublishedCb) { 67 | _this->onMqttPublishedCb(); 68 | } 69 | } 70 | 71 | static void mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) 72 | { 73 | MQTT_Client* client = (MQTT_Client*)args; 74 | 75 | MQTT* _this = (MQTT*)client->user_data; 76 | 77 | if (_this) { 78 | 79 | _this->_onMqttDataCb(topic, topic_len, data, data_len); 80 | } 81 | } 82 | 83 | static void mqttTimeoutCb(uint32_t *args) 84 | { 85 | MQTT_Client* client = (MQTT_Client*)args; 86 | 87 | MQTT* _this = (MQTT*)client->user_data; 88 | 89 | // if (_this && _this->onMqttTimeoutCb) { 90 | // _this->onMqttTimeoutCb(); 91 | // } 92 | } 93 | 94 | 95 | //------------------------------------------------------------------------------------ 96 | // MQTT class implementation 97 | //------------------------------------------------------------------------------------ 98 | MQTT::MQTT(const char* client_id, const char* host, uint32_t port) : 99 | onMqttConnectedCb(0) 100 | ,onMqttDisconnectedCb(0) 101 | ,onMqttPublishedCb(0) 102 | ,onMqttDataCb(0) 103 | ,onMqttDataRawCb(0) 104 | { 105 | // init connections 106 | MQTT_InitConnection(&mqttClient, (uint8_t*)host, port, 0); 107 | 108 | // init client 109 | if ( !MQTT_InitClient(&mqttClient, (uint8_t*)client_id, (uint8_t*)"", (uint8_t*)"", 120, 1) ) { 110 | MQTT_INFO("Failed to initialize properly. Check MQTT version.\r\n"); 111 | } 112 | 113 | // init LWT 114 | MQTT_InitLWT(&mqttClient, (uint8_t*)"/lwt", (uint8_t*)"offline", 0, 0); 115 | 116 | // set user data 117 | mqttClient.user_data = (void*)this; 118 | 119 | // setup callbacks 120 | MQTT_OnConnected(&mqttClient, mqttConnectedCb); 121 | MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); 122 | MQTT_OnPublished(&mqttClient, mqttPublishedCb); 123 | MQTT_OnData(&mqttClient, mqttDataCb); 124 | 125 | MQTT_OnTimeout(&mqttClient, mqttTimeoutCb); 126 | } 127 | 128 | 129 | MQTT::~MQTT() 130 | { 131 | MQTT_DeleteClient(&mqttClient); 132 | } 133 | 134 | 135 | /* 136 | */ 137 | void MQTT::setClientId(const char* client_id) 138 | { 139 | MQTT_SetUserId(&mqttClient, client_id); 140 | } 141 | 142 | void MQTT::setUserPwd(const char* user, const char* pwd) 143 | { 144 | MQTT_SetUserPwd(&mqttClient, user, pwd); 145 | } 146 | 147 | 148 | /* 149 | */ 150 | void MQTT::connect() 151 | { 152 | MQTT_Connect(&mqttClient); 153 | } 154 | 155 | void MQTT::disconnect() 156 | { 157 | MQTT_Disconnect(&mqttClient); 158 | } 159 | 160 | bool MQTT::isConnected() 161 | { 162 | return (mqttClient.connState >= TCP_CONNECTED); 163 | } 164 | 165 | /* 166 | */ 167 | bool MQTT::publish(const char* topic, const char* buf, uint32_t buf_len, int qos, int retain) 168 | { 169 | return MQTT_Publish(&mqttClient, topic, buf, buf_len, qos, retain); 170 | } 171 | 172 | bool MQTT::publish(String& topic, String& data, int qos, int retain) 173 | { 174 | return publish(topic.c_str(), data.c_str(), data.length(), qos, retain); 175 | } 176 | 177 | bool MQTT::publish(String& topic, const char* buf, uint32_t buf_len, int qos, int retain) 178 | { 179 | return publish(topic.c_str(), buf, buf_len, qos, retain); 180 | } 181 | 182 | bool MQTT::publish(const char* topic, String& data, int qos, int retain) 183 | { 184 | return publish(topic, data.c_str(), data.length(), qos, retain); 185 | } 186 | 187 | 188 | /* 189 | */ 190 | bool MQTT::subscribe(const char* topic, uint8_t qos) 191 | { 192 | return MQTT_Subscribe(&mqttClient, (char*)topic, qos); 193 | } 194 | 195 | bool MQTT::subscribe(const String& topic, uint8_t qos) 196 | { 197 | return MQTT_Subscribe(&mqttClient, (char*)topic.c_str(), qos); 198 | } 199 | 200 | 201 | 202 | //------------------------------------------------------------------------------- 203 | // set user callback functions 204 | //------------------------------------------------------------------------------- 205 | void MQTT::onConnected( void (*function)(void) ) 206 | { 207 | onMqttConnectedCb = function; 208 | } 209 | 210 | void MQTT::onDisconnected( void (*function)(void) ) 211 | { 212 | onMqttDisconnectedCb = function; 213 | } 214 | 215 | void MQTT::onPublished( void (*function)(void) ) 216 | { 217 | onMqttPublishedCb = function; 218 | } 219 | 220 | void MQTT::onData( void (*function)(String&, String&) ) 221 | { 222 | onMqttDataCb = function; 223 | } 224 | 225 | void MQTT::onData( void (*function)(const char*, uint32_t, const char*, uint32_t) ) 226 | { 227 | onMqttDataRawCb = function; 228 | } 229 | 230 | 231 | // internal callback, calling user CB 232 | void MQTT::_onMqttDataCb(const char* topic, uint32_t topic_len, const char* buf, uint32_t buf_len) 233 | { 234 | if (onMqttDataRawCb) { 235 | onMqttDataRawCb(topic, topic_len, buf, buf_len); 236 | } 237 | 238 | if (onMqttDataCb) { 239 | 240 | char* topicCpy = (char*)malloc(topic_len+1); 241 | memcpy(topicCpy, topic, topic_len); 242 | topicCpy[topic_len] = 0; 243 | // string it 244 | String topicStr(topicCpy); 245 | 246 | char* bufCpy = (char*)malloc(buf_len+1); 247 | memcpy(bufCpy, buf, buf_len); 248 | bufCpy[buf_len] = 0; 249 | // string it 250 | String bufStr(bufCpy); 251 | 252 | onMqttDataCb(topicStr, bufStr); 253 | 254 | free(topicCpy); 255 | free(bufCpy); 256 | } 257 | } 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /src/mqtt/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 | #include "mqtt_config.h" 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 | #if defined(PROTOCOL_NAMEv31) 51 | uint8_t magic[6]; 52 | #elif defined(PROTOCOL_NAMEv311) 53 | uint8_t magic[4]; 54 | #else 55 | #error "Please define protocol name" 56 | #endif 57 | uint8_t version; 58 | uint8_t flags; 59 | uint8_t keepaliveMsb; 60 | uint8_t keepaliveLsb; 61 | }; 62 | 63 | static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len) 64 | { 65 | if (connection->message.length + len + 2 > connection->buffer_length) 66 | return -1; 67 | 68 | connection->buffer[connection->message.length++] = len >> 8; 69 | connection->buffer[connection->message.length++] = len & 0xff; 70 | memcpy(connection->buffer + connection->message.length, string, len); 71 | connection->message.length += len; 72 | 73 | return len + 2; 74 | } 75 | 76 | static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id) 77 | { 78 | // If message_id is zero then we should assign one, otherwise 79 | // we'll use the one supplied by the caller 80 | while (message_id == 0) 81 | message_id = ++connection->message_id; 82 | 83 | if (connection->message.length + 2 > connection->buffer_length) 84 | return 0; 85 | 86 | connection->buffer[connection->message.length++] = message_id >> 8; 87 | connection->buffer[connection->message.length++] = message_id & 0xff; 88 | 89 | return message_id; 90 | } 91 | 92 | static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection) 93 | { 94 | connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; 95 | return MQTT_MAX_FIXED_HEADER_SIZE; 96 | } 97 | 98 | static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection) 99 | { 100 | connection->message.data = connection->buffer; 101 | connection->message.length = 0; 102 | return &connection->message; 103 | } 104 | 105 | static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) 106 | { 107 | int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; 108 | 109 | if (remaining_length > 127) 110 | { 111 | connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); 112 | connection->buffer[1] = 0x80 | (remaining_length % 128); 113 | connection->buffer[2] = remaining_length / 128; 114 | connection->message.length = remaining_length + 3; 115 | connection->message.data = connection->buffer; 116 | } 117 | else 118 | { 119 | connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); 120 | connection->buffer[2] = remaining_length; 121 | connection->message.length = remaining_length + 2; 122 | connection->message.data = connection->buffer + 1; 123 | } 124 | 125 | return &connection->message; 126 | } 127 | 128 | void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) 129 | { 130 | memset(connection, 0, sizeof(mqtt_connection_t)); 131 | connection->buffer = buffer; 132 | connection->buffer_length = buffer_length; 133 | } 134 | 135 | int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length) 136 | { 137 | int i; 138 | int totlen = 0; 139 | 140 | for (i = 1; i < length; ++i) 141 | { 142 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 143 | if ((buffer[i] & 0x80) == 0) 144 | { 145 | ++i; 146 | break; 147 | } 148 | } 149 | totlen += i; 150 | 151 | return totlen; 152 | } 153 | 154 | const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) 155 | { 156 | int i; 157 | int totlen = 0; 158 | int topiclen; 159 | 160 | for (i = 1; i < *length; ++i) 161 | { 162 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 163 | if ((buffer[i] & 0x80) == 0) 164 | { 165 | ++i; 166 | break; 167 | } 168 | } 169 | totlen += i; 170 | 171 | if (i + 2 >= *length) 172 | return NULL; 173 | topiclen = buffer[i++] << 8; 174 | topiclen |= buffer[i++]; 175 | 176 | if (i + topiclen > *length) 177 | return NULL; 178 | 179 | *length = topiclen; 180 | return (const char*)(buffer + i); 181 | } 182 | 183 | const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) 184 | { 185 | int i; 186 | int totlen = 0; 187 | int topiclen; 188 | int blength = *length; 189 | *length = 0; 190 | 191 | for (i = 1; i < blength; ++i) 192 | { 193 | totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); 194 | if ((buffer[i] & 0x80) == 0) 195 | { 196 | ++i; 197 | break; 198 | } 199 | } 200 | totlen += i; 201 | 202 | if (i + 2 >= blength) 203 | return NULL; 204 | topiclen = buffer[i++] << 8; 205 | topiclen |= buffer[i++]; 206 | 207 | if (i + topiclen >= blength) 208 | return NULL; 209 | 210 | i += topiclen; 211 | 212 | if (mqtt_get_qos(buffer) > 0) 213 | { 214 | if (i + 2 >= blength) 215 | return NULL; 216 | i += 2; 217 | } 218 | 219 | if (totlen < i) 220 | return NULL; 221 | 222 | if (totlen <= blength) 223 | *length = totlen - i; 224 | else 225 | *length = blength - i; 226 | return (const char*)(buffer + i); 227 | } 228 | 229 | uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length) 230 | { 231 | if (length < 1) 232 | return 0; 233 | 234 | switch (mqtt_get_type(buffer)) 235 | { 236 | case MQTT_MSG_TYPE_PUBLISH: 237 | { 238 | int i; 239 | int topiclen; 240 | 241 | for (i = 1; i < length; ++i) 242 | { 243 | if ((buffer[i] & 0x80) == 0) 244 | { 245 | ++i; 246 | break; 247 | } 248 | } 249 | 250 | if (i + 2 >= length) 251 | return 0; 252 | topiclen = buffer[i++] << 8; 253 | topiclen |= buffer[i++]; 254 | 255 | if (i + topiclen >= length) 256 | return 0; 257 | i += topiclen; 258 | 259 | if (mqtt_get_qos(buffer) > 0) 260 | { 261 | if (i + 2 >= length) 262 | return 0; 263 | //i += 2; 264 | } else { 265 | return 0; 266 | } 267 | 268 | return (buffer[i] << 8) | buffer[i + 1]; 269 | } 270 | case MQTT_MSG_TYPE_PUBACK: 271 | case MQTT_MSG_TYPE_PUBREC: 272 | case MQTT_MSG_TYPE_PUBREL: 273 | case MQTT_MSG_TYPE_PUBCOMP: 274 | case MQTT_MSG_TYPE_SUBACK: 275 | case MQTT_MSG_TYPE_UNSUBACK: 276 | case MQTT_MSG_TYPE_SUBSCRIBE: 277 | { 278 | // This requires the remaining length to be encoded in 1 byte, 279 | // which it should be. 280 | if (length >= 4 && (buffer[1] & 0x80) == 0) 281 | return (buffer[2] << 8) | buffer[3]; 282 | else 283 | return 0; 284 | } 285 | 286 | default: 287 | return 0; 288 | } 289 | } 290 | 291 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) 292 | { 293 | struct mqtt_connect_variable_header* variable_header; 294 | 295 | init_message(connection); 296 | 297 | if (connection->message.length + sizeof(*variable_header) > connection->buffer_length) 298 | return fail_message(connection); 299 | variable_header = (void*)(connection->buffer + connection->message.length); 300 | connection->message.length += sizeof(*variable_header); 301 | 302 | variable_header->lengthMsb = 0; 303 | #if defined(PROTOCOL_NAMEv31) 304 | variable_header->lengthLsb = 6; 305 | memcpy(variable_header->magic, "MQIsdp", 6); 306 | variable_header->version = 3; 307 | #elif defined(PROTOCOL_NAMEv311) 308 | variable_header->lengthLsb = 4; 309 | memcpy(variable_header->magic, "MQTT", 4); 310 | variable_header->version = 4; 311 | #else 312 | #error "Please define protocol name" 313 | #endif 314 | 315 | variable_header->flags = 0; 316 | variable_header->keepaliveMsb = info->keepalive >> 8; 317 | variable_header->keepaliveLsb = info->keepalive & 0xff; 318 | 319 | if (info->clean_session) 320 | variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; 321 | 322 | if (info->client_id == NULL) 323 | { 324 | /* Never allowed */ 325 | return fail_message(connection); 326 | } 327 | else if (info->client_id[0] == '\0') 328 | { 329 | #ifdef PROTOCOL_NAMEv311 330 | /* Allowed. Format 0 Length ID */ 331 | append_string(connection, info->client_id, 2) ; 332 | #else 333 | /* 0 Length not allowed */ 334 | return fail_message(connection); 335 | #endif 336 | } 337 | else 338 | { 339 | /* No 0 data and at least 1 long. Good to go. */ 340 | if(append_string(connection, info->client_id, strlen(info->client_id)) < 0) 341 | return fail_message(connection); 342 | } 343 | 344 | if (info->will_topic != NULL && info->will_topic[0] != '\0') 345 | { 346 | if (append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) 347 | return fail_message(connection); 348 | 349 | if (append_string(connection, info->will_message, strlen(info->will_message)) < 0) 350 | return fail_message(connection); 351 | 352 | variable_header->flags |= MQTT_CONNECT_FLAG_WILL; 353 | if (info->will_retain) 354 | variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; 355 | variable_header->flags |= (info->will_qos & 3) << 3; 356 | } 357 | 358 | if (info->username != NULL && info->username[0] != '\0') 359 | { 360 | if (append_string(connection, info->username, strlen(info->username)) < 0) 361 | return fail_message(connection); 362 | 363 | variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; 364 | } 365 | 366 | if (info->password != NULL && info->password[0] != '\0') 367 | { 368 | if (append_string(connection, info->password, strlen(info->password)) < 0) 369 | return fail_message(connection); 370 | 371 | variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; 372 | } 373 | 374 | return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); 375 | } 376 | 377 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) 378 | { 379 | init_message(connection); 380 | 381 | if (topic == NULL || topic[0] == '\0') 382 | return fail_message(connection); 383 | 384 | if (append_string(connection, topic, strlen(topic)) < 0) 385 | return fail_message(connection); 386 | 387 | if (qos > 0) 388 | { 389 | if ((*message_id = append_message_id(connection, 0)) == 0) 390 | return fail_message(connection); 391 | } 392 | else 393 | *message_id = 0; 394 | 395 | if (connection->message.length + data_length > connection->buffer_length) 396 | return fail_message(connection); 397 | memcpy(connection->buffer + connection->message.length, data, data_length); 398 | connection->message.length += data_length; 399 | 400 | return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); 401 | } 402 | 403 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) 404 | { 405 | init_message(connection); 406 | if (append_message_id(connection, message_id) == 0) 407 | return fail_message(connection); 408 | return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); 409 | } 410 | 411 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) 412 | { 413 | init_message(connection); 414 | if (append_message_id(connection, message_id) == 0) 415 | return fail_message(connection); 416 | return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); 417 | } 418 | 419 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) 420 | { 421 | init_message(connection); 422 | if (append_message_id(connection, message_id) == 0) 423 | return fail_message(connection); 424 | return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); 425 | } 426 | 427 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) 428 | { 429 | init_message(connection); 430 | if (append_message_id(connection, message_id) == 0) 431 | return fail_message(connection); 432 | return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); 433 | } 434 | 435 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) 436 | { 437 | init_message(connection); 438 | 439 | if (topic == NULL || topic[0] == '\0') 440 | return fail_message(connection); 441 | 442 | if ((*message_id = append_message_id(connection, 0)) == 0) 443 | return fail_message(connection); 444 | 445 | if (append_string(connection, topic, strlen(topic)) < 0) 446 | return fail_message(connection); 447 | 448 | if (connection->message.length + 1 > connection->buffer_length) 449 | return fail_message(connection); 450 | connection->buffer[connection->message.length++] = qos; 451 | 452 | return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); 453 | } 454 | 455 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) 456 | { 457 | init_message(connection); 458 | 459 | if (topic == NULL || topic[0] == '\0') 460 | return fail_message(connection); 461 | 462 | if ((*message_id = append_message_id(connection, 0)) == 0) 463 | return fail_message(connection); 464 | 465 | if (append_string(connection, topic, strlen(topic)) < 0) 466 | return fail_message(connection); 467 | 468 | return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0); 469 | } 470 | 471 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection) 472 | { 473 | init_message(connection); 474 | return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); 475 | } 476 | 477 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection) 478 | { 479 | init_message(connection); 480 | return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); 481 | } 482 | 483 | mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection) 484 | { 485 | init_message(connection); 486 | return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); 487 | } 488 | -------------------------------------------------------------------------------- /src/mqtt/mqtt.c: -------------------------------------------------------------------------------- 1 | /* mqtt.c 2 | * Protocol: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html 3 | * 4 | * Copyright (c) 2014-2015, Tuan PM 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include "user_interface.h" 33 | #include "osapi.h" 34 | #include "espconn.h" 35 | #include "os_type.h" 36 | #include "mem.h" 37 | #include "mqtt_msg.h" 38 | #include "debug.h" 39 | #include "mqtt.h" 40 | #include "queue.h" 41 | 42 | #define MQTT_TASK_PRIO 1 43 | #define MQTT_TASK_QUEUE_SIZE 1 44 | #define MQTT_SEND_TIMOUT 5 45 | 46 | #ifndef MQTT_SSL_SIZE 47 | #define MQTT_SSL_SIZE 5120 48 | #endif 49 | 50 | #ifndef QUEUE_BUFFER_SIZE 51 | #define QUEUE_BUFFER_SIZE 2048 52 | #endif 53 | 54 | 55 | os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; 56 | 57 | #ifdef PROTOCOL_NAMEv311 58 | LOCAL uint8_t zero_len_id[2] = { 0, 0 }; 59 | #endif 60 | 61 | LOCAL void ICACHE_FLASH_ATTR 62 | mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) 63 | { 64 | struct espconn *pConn = (struct espconn *)arg; 65 | MQTT_Client* client = (MQTT_Client *)pConn->reverse; 66 | 67 | 68 | if (ipaddr == NULL) 69 | { 70 | MQTT_INFO("DNS: Found, but got no ip, try to reconnect\r\n"); 71 | client->connState = TCP_RECONNECT_REQ; 72 | return; 73 | } 74 | 75 | MQTT_INFO("DNS: found ip %d.%d.%d.%d\n", 76 | *((uint8 *) &ipaddr->addr), 77 | *((uint8 *) &ipaddr->addr + 1), 78 | *((uint8 *) &ipaddr->addr + 2), 79 | *((uint8 *) &ipaddr->addr + 3)); 80 | 81 | if (client->ip.addr == 0 && ipaddr->addr != 0) 82 | { 83 | os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); 84 | if (client->security) { 85 | #ifdef MQTT_SSL_ENABLE 86 | espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE); 87 | espconn_secure_connect(client->pCon); 88 | #else 89 | MQTT_INFO("TCP: Do not support SSL\r\n"); 90 | #endif 91 | } 92 | else { 93 | espconn_connect(client->pCon); 94 | } 95 | 96 | client->connState = TCP_CONNECTING; 97 | MQTT_INFO("TCP: connecting...\r\n"); 98 | } 99 | 100 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 101 | } 102 | 103 | 104 | 105 | LOCAL void ICACHE_FLASH_ATTR 106 | deliver_publish(MQTT_Client* client, uint8_t* message, int length) 107 | { 108 | mqtt_event_data_t event_data; 109 | 110 | event_data.topic_length = length; 111 | event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); 112 | event_data.data_length = length; 113 | event_data.data = mqtt_get_publish_data(message, &event_data.data_length); 114 | 115 | if (client->dataCb) 116 | client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); 117 | 118 | } 119 | 120 | void ICACHE_FLASH_ATTR 121 | mqtt_send_keepalive(MQTT_Client *client) 122 | { 123 | MQTT_INFO("\r\nMQTT: Send keepalive packet to %s:%d!\r\n", client->host, client->port); 124 | client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); 125 | client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ; 126 | client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); 127 | client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 128 | 129 | 130 | client->sendTimeout = MQTT_SEND_TIMOUT; 131 | MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); 132 | err_t result = ESPCONN_OK; 133 | if (client->security) { 134 | #ifdef MQTT_SSL_ENABLE 135 | result = espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 136 | #else 137 | MQTT_INFO("TCP: Do not support SSL\r\n"); 138 | #endif 139 | } 140 | else { 141 | result = espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 142 | } 143 | 144 | client->mqtt_state.outbound_message = NULL; 145 | if (ESPCONN_OK == result) { 146 | client->keepAliveTick = 0; 147 | client->connState = MQTT_DATA; 148 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 149 | } 150 | else { 151 | client->connState = TCP_RECONNECT_DISCONNECTING; 152 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 153 | } 154 | } 155 | 156 | /** 157 | * @brief Delete tcp client and free all memory 158 | * @param mqttClient: The mqtt client which contain TCP client 159 | * @retval None 160 | */ 161 | void ICACHE_FLASH_ATTR 162 | mqtt_tcpclient_delete(MQTT_Client *mqttClient) 163 | { 164 | if (mqttClient->pCon != NULL) { 165 | MQTT_INFO("TCP: Free memory\r\n"); 166 | // Force abort connections 167 | espconn_abort(mqttClient->pCon); 168 | // Delete connections 169 | espconn_delete(mqttClient->pCon); 170 | 171 | if (mqttClient->pCon->proto.tcp) { 172 | os_free(mqttClient->pCon->proto.tcp); 173 | mqttClient->pCon->proto.tcp = NULL; 174 | } 175 | os_free(mqttClient->pCon); 176 | mqttClient->pCon = NULL; 177 | } 178 | } 179 | 180 | /** 181 | * @brief Delete MQTT client and free all memory 182 | * @param mqttClient: The mqtt client 183 | * @retval None 184 | */ 185 | void ICACHE_FLASH_ATTR 186 | mqtt_client_delete(MQTT_Client *mqttClient) 187 | { 188 | if (mqttClient == NULL) 189 | return; 190 | 191 | if (mqttClient->pCon != NULL) { 192 | mqtt_tcpclient_delete(mqttClient); 193 | } 194 | 195 | if (mqttClient->host != NULL) { 196 | os_free(mqttClient->host); 197 | mqttClient->host = NULL; 198 | } 199 | 200 | if (mqttClient->user_data != NULL) { 201 | os_free(mqttClient->user_data); 202 | mqttClient->user_data = NULL; 203 | } 204 | 205 | if (mqttClient->mqtt_state.in_buffer != NULL) { 206 | os_free(mqttClient->mqtt_state.in_buffer); 207 | mqttClient->mqtt_state.in_buffer = NULL; 208 | } 209 | 210 | if (mqttClient->mqtt_state.out_buffer != NULL) { 211 | os_free(mqttClient->mqtt_state.out_buffer); 212 | mqttClient->mqtt_state.out_buffer = NULL; 213 | } 214 | 215 | if (mqttClient->mqtt_state.outbound_message != NULL) { 216 | if (mqttClient->mqtt_state.outbound_message->data != NULL) 217 | { 218 | os_free(mqttClient->mqtt_state.outbound_message->data); 219 | mqttClient->mqtt_state.outbound_message->data = NULL; 220 | } 221 | } 222 | 223 | if (mqttClient->mqtt_state.mqtt_connection.buffer != NULL) { 224 | // Already freed but not NULL 225 | mqttClient->mqtt_state.mqtt_connection.buffer = NULL; 226 | } 227 | 228 | if (mqttClient->connect_info.client_id != NULL) { 229 | #ifdef PROTOCOL_NAMEv311 230 | /* Don't attempt to free if it's the zero_len array */ 231 | if ( ((uint8_t*)mqttClient->connect_info.client_id) != zero_len_id ) 232 | os_free(mqttClient->connect_info.client_id); 233 | #else 234 | os_free(mqttClient->connect_info.client_id); 235 | #endif 236 | mqttClient->connect_info.client_id = NULL; 237 | } 238 | 239 | if (mqttClient->connect_info.username != NULL) { 240 | os_free(mqttClient->connect_info.username); 241 | mqttClient->connect_info.username = NULL; 242 | } 243 | 244 | if (mqttClient->connect_info.password != NULL) { 245 | os_free(mqttClient->connect_info.password); 246 | mqttClient->connect_info.password = NULL; 247 | } 248 | 249 | if (mqttClient->connect_info.will_topic != NULL) { 250 | os_free(mqttClient->connect_info.will_topic); 251 | mqttClient->connect_info.will_topic = NULL; 252 | } 253 | 254 | if (mqttClient->connect_info.will_message != NULL) { 255 | os_free(mqttClient->connect_info.will_message); 256 | mqttClient->connect_info.will_message = NULL; 257 | } 258 | 259 | if (mqttClient->msgQueue.buf != NULL) { 260 | os_free(mqttClient->msgQueue.buf); 261 | mqttClient->msgQueue.buf = NULL; 262 | } 263 | 264 | // Initialize state 265 | mqttClient->connState = WIFI_INIT; 266 | // Clear callback functions to avoid abnormal callback 267 | mqttClient->connectedCb = NULL; 268 | mqttClient->disconnectedCb = NULL; 269 | mqttClient->publishedCb = NULL; 270 | mqttClient->timeoutCb = NULL; 271 | mqttClient->dataCb = NULL; 272 | 273 | MQTT_INFO("MQTT: client already deleted\r\n"); 274 | } 275 | 276 | 277 | /** 278 | * @brief Client received callback function. 279 | * @param arg: contain the ip link information 280 | * @param pdata: received data 281 | * @param len: the lenght of received data 282 | * @retval None 283 | */ 284 | void ICACHE_FLASH_ATTR 285 | mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len) 286 | { 287 | uint8_t msg_type; 288 | uint8_t msg_qos; 289 | uint16_t msg_id; 290 | uint8_t msg_conn_ret; 291 | 292 | struct espconn *pCon = (struct espconn*)arg; 293 | MQTT_Client *client = (MQTT_Client *)pCon->reverse; 294 | 295 | READPACKET: 296 | MQTT_INFO("TCP: data received %d bytes\r\n", len); 297 | // MQTT_INFO("STATE: %d\r\n", client->connState); 298 | if (len < MQTT_BUF_SIZE && len > 0) { 299 | os_memcpy(client->mqtt_state.in_buffer, pdata, len); 300 | 301 | msg_type = mqtt_get_type(client->mqtt_state.in_buffer); 302 | msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); 303 | msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); 304 | switch (client->connState) { 305 | case MQTT_CONNECT_SENDING: 306 | if (msg_type == MQTT_MSG_TYPE_CONNACK) { 307 | if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT) { 308 | MQTT_INFO("MQTT: Invalid packet\r\n"); 309 | if (client->security) { 310 | #ifdef MQTT_SSL_ENABLE 311 | espconn_secure_disconnect(client->pCon); 312 | #else 313 | MQTT_INFO("TCP: Do not support SSL\r\n"); 314 | #endif 315 | } 316 | else { 317 | espconn_disconnect(client->pCon); 318 | } 319 | } else { 320 | msg_conn_ret = mqtt_get_connect_return_code(client->mqtt_state.in_buffer); 321 | switch (msg_conn_ret) { 322 | case CONNECTION_ACCEPTED: 323 | MQTT_INFO("MQTT: Connected to %s:%d\r\n", client->host, client->port); 324 | client->connState = MQTT_DATA; 325 | if (client->connectedCb) 326 | client->connectedCb((uint32_t*)client); 327 | break; 328 | case CONNECTION_REFUSE_PROTOCOL: 329 | case CONNECTION_REFUSE_SERVER_UNAVAILABLE: 330 | case CONNECTION_REFUSE_BAD_USERNAME: 331 | case CONNECTION_REFUSE_NOT_AUTHORIZED: 332 | MQTT_INFO("MQTT: Connection refuse, reason code: %d\r\n", msg_conn_ret); 333 | default: 334 | if (client->security) { 335 | #ifdef MQTT_SSL_ENABLE 336 | espconn_secure_disconnect(client->pCon); 337 | #else 338 | MQTT_INFO("TCP: Do not support SSL\r\n"); 339 | #endif 340 | } 341 | else { 342 | espconn_disconnect(client->pCon); 343 | } 344 | 345 | } 346 | 347 | } 348 | 349 | } 350 | break; 351 | case MQTT_DATA: 352 | case MQTT_KEEPALIVE_SEND: 353 | client->mqtt_state.message_length_read = len; 354 | client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); 355 | 356 | 357 | switch (msg_type) 358 | { 359 | 360 | case MQTT_MSG_TYPE_SUBACK: 361 | if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) 362 | MQTT_INFO("MQTT: Subscribe successful\r\n"); 363 | break; 364 | case MQTT_MSG_TYPE_UNSUBACK: 365 | if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) 366 | MQTT_INFO("MQTT: UnSubscribe successful\r\n"); 367 | break; 368 | case MQTT_MSG_TYPE_PUBLISH: 369 | if (msg_qos == 1) 370 | client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); 371 | else if (msg_qos == 2) 372 | client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); 373 | if (msg_qos == 1 || msg_qos == 2) { 374 | MQTT_INFO("MQTT: Queue response QoS: %d\r\n", msg_qos); 375 | if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { 376 | MQTT_INFO("MQTT: Queue full\r\n"); 377 | } 378 | } 379 | 380 | deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); 381 | break; 382 | case MQTT_MSG_TYPE_PUBACK: 383 | if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { 384 | MQTT_INFO("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\r\n"); 385 | } 386 | 387 | break; 388 | case MQTT_MSG_TYPE_PUBREC: 389 | client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); 390 | if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { 391 | MQTT_INFO("MQTT: Queue full\r\n"); 392 | } 393 | break; 394 | case MQTT_MSG_TYPE_PUBREL: 395 | client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); 396 | if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { 397 | MQTT_INFO("MQTT: Queue full\r\n"); 398 | } 399 | break; 400 | case MQTT_MSG_TYPE_PUBCOMP: 401 | if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { 402 | MQTT_INFO("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\r\n"); 403 | } 404 | break; 405 | case MQTT_MSG_TYPE_PINGREQ: 406 | client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); 407 | if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { 408 | MQTT_INFO("MQTT: Queue full\r\n"); 409 | } 410 | break; 411 | case MQTT_MSG_TYPE_PINGRESP: 412 | // Ignore 413 | break; 414 | } 415 | // NOTE: this is done down here and not in the switch case above 416 | // because the PSOCK_READBUF_LEN() won't work inside a switch 417 | // statement due to the way protothreads resume. 418 | if (msg_type == MQTT_MSG_TYPE_PUBLISH) 419 | { 420 | len = client->mqtt_state.message_length_read; 421 | 422 | if (client->mqtt_state.message_length < client->mqtt_state.message_length_read) 423 | { 424 | //client->connState = MQTT_PUBLISH_RECV; 425 | //Not Implement yet 426 | len -= client->mqtt_state.message_length; 427 | pdata += client->mqtt_state.message_length; 428 | 429 | MQTT_INFO("Get another published message\r\n"); 430 | goto READPACKET; 431 | } 432 | 433 | } 434 | break; 435 | } 436 | } else { 437 | MQTT_INFO("ERROR: Message too long\r\n"); 438 | } 439 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 440 | } 441 | 442 | /** 443 | * @brief Client send over callback function. 444 | * @param arg: contain the ip link information 445 | * @retval None 446 | */ 447 | void ICACHE_FLASH_ATTR 448 | mqtt_tcpclient_sent_cb(void *arg) 449 | { 450 | struct espconn *pCon = (struct espconn *)arg; 451 | MQTT_Client* client = (MQTT_Client *)pCon->reverse; 452 | MQTT_INFO("TCP: Sent\r\n"); 453 | client->sendTimeout = 0; 454 | client->keepAliveTick = 0; 455 | 456 | if ((client->connState == MQTT_DATA || client->connState == MQTT_KEEPALIVE_SEND) 457 | && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH) { 458 | if (client->publishedCb) 459 | client->publishedCb((uint32_t*)client); 460 | } 461 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 462 | } 463 | 464 | void ICACHE_FLASH_ATTR mqtt_timer(void *arg) 465 | { 466 | MQTT_Client* client = (MQTT_Client*)arg; 467 | 468 | if (client->connState == MQTT_DATA) { 469 | client->keepAliveTick ++; 470 | if (client->keepAliveTick > (client->mqtt_state.connect_info->keepalive / 2)) { 471 | client->connState = MQTT_KEEPALIVE_SEND; 472 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 473 | } 474 | 475 | } else if (client->connState == TCP_RECONNECT_REQ) { 476 | client->reconnectTick ++; 477 | if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { 478 | client->reconnectTick = 0; 479 | client->connState = TCP_RECONNECT; 480 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 481 | if (client->timeoutCb) 482 | client->timeoutCb((uint32_t*)client); 483 | } 484 | } 485 | if (client->sendTimeout > 0) 486 | client->sendTimeout --; 487 | } 488 | 489 | void ICACHE_FLASH_ATTR 490 | mqtt_tcpclient_discon_cb(void *arg) 491 | { 492 | 493 | struct espconn *pespconn = (struct espconn *)arg; 494 | MQTT_Client* client = (MQTT_Client *)pespconn->reverse; 495 | MQTT_INFO("TCP: Disconnected callback\r\n"); 496 | if (TCP_DISCONNECTING == client->connState) { 497 | client->connState = TCP_DISCONNECTED; 498 | } 499 | else if (MQTT_DELETING == client->connState) { 500 | client->connState = MQTT_DELETED; 501 | } 502 | else { 503 | client->connState = TCP_RECONNECT_REQ; 504 | } 505 | if (client->disconnectedCb) 506 | client->disconnectedCb((uint32_t*)client); 507 | 508 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 509 | } 510 | 511 | 512 | 513 | /** 514 | * @brief Tcp client connect success callback function. 515 | * @param arg: contain the ip link information 516 | * @retval None 517 | */ 518 | void ICACHE_FLASH_ATTR 519 | mqtt_tcpclient_connect_cb(void *arg) 520 | { 521 | struct espconn *pCon = (struct espconn *)arg; 522 | MQTT_Client* client = (MQTT_Client *)pCon->reverse; 523 | 524 | espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); 525 | espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);//////// 526 | espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);/////// 527 | MQTT_INFO("MQTT: Connected to broker %s:%d\r\n", client->host, client->port); 528 | 529 | mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); 530 | client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); 531 | client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); 532 | client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 533 | 534 | 535 | client->sendTimeout = MQTT_SEND_TIMOUT; 536 | MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); 537 | if (client->security) { 538 | #ifdef MQTT_SSL_ENABLE 539 | espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 540 | #else 541 | MQTT_INFO("TCP: Do not support SSL\r\n"); 542 | #endif 543 | } 544 | else { 545 | espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); 546 | } 547 | 548 | client->mqtt_state.outbound_message = NULL; 549 | client->connState = MQTT_CONNECT_SENDING; 550 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 551 | } 552 | 553 | /** 554 | * @brief Tcp client connect repeat callback function. 555 | * @param arg: contain the ip link information 556 | * @retval None 557 | */ 558 | void ICACHE_FLASH_ATTR 559 | mqtt_tcpclient_recon_cb(void *arg, sint8 errType) 560 | { 561 | struct espconn *pCon = (struct espconn *)arg; 562 | MQTT_Client* client = (MQTT_Client *)pCon->reverse; 563 | 564 | MQTT_INFO("TCP: Reconnect to %s:%d\r\n", client->host, client->port); 565 | 566 | client->connState = TCP_RECONNECT_REQ; 567 | 568 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 569 | 570 | } 571 | 572 | /** 573 | * @brief MQTT publish function. 574 | * @param client: MQTT_Client reference 575 | * @param topic: string topic will publish to 576 | * @param data: buffer data send point to 577 | * @param data_length: length of data 578 | * @param qos: qos 579 | * @param retain: retain 580 | * @retval TRUE if success queue 581 | */ 582 | BOOL ICACHE_FLASH_ATTR 583 | MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain) 584 | { 585 | uint8_t dataBuffer[MQTT_BUF_SIZE]; 586 | uint16_t dataLen; 587 | client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, 588 | topic, data, data_length, 589 | qos, retain, 590 | &client->mqtt_state.pending_msg_id); 591 | if (client->mqtt_state.outbound_message->length == 0) { 592 | MQTT_INFO("MQTT: Queuing publish failed\r\n"); 593 | return FALSE; 594 | } 595 | MQTT_INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size); 596 | while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { 597 | MQTT_INFO("MQTT: Queue full\r\n"); 598 | if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { 599 | MQTT_INFO("MQTT: Serious buffer error\r\n"); 600 | return FALSE; 601 | } 602 | } 603 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 604 | return TRUE; 605 | } 606 | 607 | /** 608 | * @brief MQTT subscibe function. 609 | * @param client: MQTT_Client reference 610 | * @param topic: string topic will subscribe 611 | * @param qos: qos 612 | * @retval TRUE if success queue 613 | */ 614 | BOOL ICACHE_FLASH_ATTR 615 | MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos) 616 | { 617 | uint8_t dataBuffer[MQTT_BUF_SIZE]; 618 | uint16_t dataLen; 619 | 620 | client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, 621 | topic, qos, 622 | &client->mqtt_state.pending_msg_id); 623 | MQTT_INFO("MQTT: queue subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id); 624 | while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { 625 | MQTT_INFO("MQTT: Queue full\r\n"); 626 | if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { 627 | MQTT_INFO("MQTT: Serious buffer error\r\n"); 628 | return FALSE; 629 | } 630 | } 631 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 632 | 633 | return TRUE; 634 | } 635 | 636 | /** 637 | * @brief MQTT un-subscibe function. 638 | * @param client: MQTT_Client reference 639 | * @param topic: String topic will un-subscribe 640 | * @retval TRUE if success queue 641 | */ 642 | BOOL ICACHE_FLASH_ATTR 643 | MQTT_UnSubscribe(MQTT_Client *client, char* topic) 644 | { 645 | uint8_t dataBuffer[MQTT_BUF_SIZE]; 646 | uint16_t dataLen; 647 | client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection, 648 | topic, 649 | &client->mqtt_state.pending_msg_id); 650 | MQTT_INFO("MQTT: queue un-subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id); 651 | while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { 652 | MQTT_INFO("MQTT: Queue full\r\n"); 653 | if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { 654 | MQTT_INFO("MQTT: Serious buffer error\r\n"); 655 | return FALSE; 656 | } 657 | } 658 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 659 | return TRUE; 660 | } 661 | 662 | /** 663 | * @brief MQTT ping function. 664 | * @param client: MQTT_Client reference 665 | * @retval TRUE if success queue 666 | */ 667 | BOOL ICACHE_FLASH_ATTR 668 | MQTT_Ping(MQTT_Client *client) 669 | { 670 | uint8_t dataBuffer[MQTT_BUF_SIZE]; 671 | uint16_t dataLen; 672 | client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); 673 | if (client->mqtt_state.outbound_message->length == 0) { 674 | MQTT_INFO("MQTT: Queuing publish failed\r\n"); 675 | return FALSE; 676 | } 677 | MQTT_INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size); 678 | while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { 679 | MQTT_INFO("MQTT: Queue full\r\n"); 680 | if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { 681 | MQTT_INFO("MQTT: Serious buffer error\r\n"); 682 | return FALSE; 683 | } 684 | } 685 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); 686 | return TRUE; 687 | } 688 | 689 | void ICACHE_FLASH_ATTR 690 | MQTT_Task(os_event_t *e) 691 | { 692 | MQTT_Client* client = (MQTT_Client*)e->par; 693 | uint8_t dataBuffer[MQTT_BUF_SIZE]; 694 | uint16_t dataLen; 695 | if (e->par == 0) 696 | return; 697 | switch (client->connState) { 698 | 699 | case TCP_RECONNECT_REQ: 700 | break; 701 | case TCP_RECONNECT: 702 | mqtt_tcpclient_delete(client); 703 | MQTT_Connect(client); 704 | MQTT_INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port); 705 | client->connState = TCP_CONNECTING; 706 | break; 707 | case MQTT_DELETING: 708 | case TCP_DISCONNECTING: 709 | case TCP_RECONNECT_DISCONNECTING: 710 | if (client->security) { 711 | #ifdef MQTT_SSL_ENABLE 712 | espconn_secure_disconnect(client->pCon); 713 | #else 714 | MQTT_INFO("TCP: Do not support SSL\r\n"); 715 | #endif 716 | } 717 | else { 718 | espconn_disconnect(client->pCon); 719 | } 720 | break; 721 | case TCP_DISCONNECTED: 722 | MQTT_INFO("MQTT: Disconnected\r\n"); 723 | mqtt_tcpclient_delete(client); 724 | break; 725 | case MQTT_DELETED: 726 | MQTT_INFO("MQTT: Deleted client\r\n"); 727 | mqtt_client_delete(client); 728 | break; 729 | case MQTT_KEEPALIVE_SEND: 730 | mqtt_send_keepalive(client); 731 | break; 732 | case MQTT_DATA: 733 | if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) { 734 | break; 735 | } 736 | if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0) { 737 | client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); 738 | client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); 739 | 740 | 741 | client->sendTimeout = MQTT_SEND_TIMOUT; 742 | MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); 743 | client->keepAliveTick = 0; 744 | if (client->security) { 745 | #ifdef MQTT_SSL_ENABLE 746 | espconn_secure_send(client->pCon, dataBuffer, dataLen); 747 | #else 748 | MQTT_INFO("TCP: Do not support SSL\r\n"); 749 | #endif 750 | } 751 | else { 752 | espconn_send(client->pCon, dataBuffer, dataLen); 753 | } 754 | 755 | client->mqtt_state.outbound_message = NULL; 756 | break; 757 | } 758 | break; 759 | } 760 | } 761 | 762 | /** 763 | * @brief MQTT initialization connection function 764 | * @param client: MQTT_Client reference 765 | * @param host: Domain or IP string 766 | * @param port: Port to connect 767 | * @param security: 1 for ssl, 0 for none 768 | * @retval None 769 | */ 770 | void ICACHE_FLASH_ATTR 771 | MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security) 772 | { 773 | uint32_t temp; 774 | MQTT_INFO("MQTT:InitConnection\r\n"); 775 | os_memset(mqttClient, 0, sizeof(MQTT_Client)); 776 | temp = os_strlen(host); 777 | mqttClient->host = (uint8_t*)os_zalloc(temp + 1); 778 | os_strcpy(mqttClient->host, host); 779 | mqttClient->host[temp] = 0; 780 | mqttClient->port = port; 781 | mqttClient->security = security; 782 | 783 | } 784 | 785 | /** 786 | * @brief MQTT initialization mqtt client function 787 | * @param client: MQTT_Client reference 788 | * @param clientid: MQTT client id 789 | * @param client_user:MQTT client user 790 | * @param client_pass:MQTT client password 791 | * @param client_pass:MQTT keep alive timer, in second 792 | * @retval None 793 | */ 794 | BOOL ICACHE_FLASH_ATTR 795 | MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession) 796 | { 797 | uint32_t temp; 798 | MQTT_INFO("MQTT:InitClient\r\n"); 799 | 800 | os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); 801 | 802 | if ( !client_id ) 803 | { 804 | /* Should be allowed by broker, but clean session flag must be set. */ 805 | #ifdef PROTOCOL_NAMEv311 806 | if (cleanSession) 807 | { 808 | mqttClient->connect_info.client_id = zero_len_id; 809 | } else { 810 | MQTT_INFO("cleanSession must be set to use 0 length client_id\r\n"); 811 | return false; 812 | } 813 | /* Not supported. Return. */ 814 | #else 815 | MQTT_INFO("Client ID required for MQTT < 3.1.1!\r\n"); 816 | return false; 817 | #endif 818 | } 819 | 820 | /* If connect_info's client_id is still NULL and we get here, we can * 821 | * assume the passed client_id is non-NULL. */ 822 | if ( !(mqttClient->connect_info.client_id) ) 823 | { 824 | temp = os_strlen(client_id); 825 | mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1); 826 | os_strcpy(mqttClient->connect_info.client_id, client_id); 827 | mqttClient->connect_info.client_id[temp] = 0; 828 | } 829 | 830 | if (client_user) 831 | { 832 | temp = os_strlen(client_user); 833 | mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1); 834 | os_strcpy(mqttClient->connect_info.username, client_user); 835 | mqttClient->connect_info.username[temp] = 0; 836 | } 837 | 838 | if (client_pass) 839 | { 840 | temp = os_strlen(client_pass); 841 | mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1); 842 | os_strcpy(mqttClient->connect_info.password, client_pass); 843 | mqttClient->connect_info.password[temp] = 0; 844 | } 845 | 846 | 847 | mqttClient->connect_info.keepalive = keepAliveTime; 848 | mqttClient->connect_info.clean_session = cleanSession; 849 | 850 | mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); 851 | mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; 852 | mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); 853 | mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; 854 | mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; 855 | 856 | mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length); 857 | 858 | QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); 859 | 860 | system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); 861 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); 862 | return true; 863 | } 864 | void ICACHE_FLASH_ATTR 865 | MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain) 866 | { 867 | uint32_t temp; 868 | temp = os_strlen(will_topic); 869 | mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1); 870 | os_strcpy(mqttClient->connect_info.will_topic, will_topic); 871 | mqttClient->connect_info.will_topic[temp] = 0; 872 | 873 | temp = os_strlen(will_msg); 874 | mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1); 875 | os_strcpy(mqttClient->connect_info.will_message, will_msg); 876 | mqttClient->connect_info.will_message[temp] = 0; 877 | 878 | 879 | mqttClient->connect_info.will_qos = will_qos; 880 | mqttClient->connect_info.will_retain = will_retain; 881 | } 882 | /** 883 | * @brief Begin connect to MQTT broker 884 | * @param client: MQTT_Client reference 885 | * @retval None 886 | */ 887 | void ICACHE_FLASH_ATTR 888 | MQTT_Connect(MQTT_Client *mqttClient) 889 | { 890 | if (mqttClient->pCon) { 891 | // Clean up the old connection forcefully - using MQTT_Disconnect 892 | // does not actually release the old connection until the 893 | // disconnection callback is invoked. 894 | mqtt_tcpclient_delete(mqttClient); 895 | } 896 | mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); 897 | mqttClient->pCon->type = ESPCONN_TCP; 898 | mqttClient->pCon->state = ESPCONN_NONE; 899 | mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); 900 | mqttClient->pCon->proto.tcp->local_port = espconn_port(); 901 | mqttClient->pCon->proto.tcp->remote_port = mqttClient->port; 902 | mqttClient->pCon->reverse = mqttClient; 903 | espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); 904 | espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); 905 | 906 | mqttClient->keepAliveTick = 0; 907 | mqttClient->reconnectTick = 0; 908 | 909 | 910 | os_timer_disarm(&mqttClient->mqttTimer); 911 | os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); 912 | os_timer_arm(&mqttClient->mqttTimer, 1000, 1); 913 | 914 | if (UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip)) { 915 | MQTT_INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port); 916 | if (mqttClient->security) 917 | { 918 | #ifdef MQTT_SSL_ENABLE 919 | espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE); 920 | espconn_secure_connect(mqttClient->pCon); 921 | #else 922 | MQTT_INFO("TCP: Do not support SSL\r\n"); 923 | #endif 924 | } 925 | else 926 | { 927 | espconn_connect(mqttClient->pCon); 928 | } 929 | } 930 | else { 931 | MQTT_INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port); 932 | espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found); 933 | } 934 | mqttClient->connState = TCP_CONNECTING; 935 | } 936 | 937 | void ICACHE_FLASH_ATTR 938 | MQTT_Disconnect(MQTT_Client *mqttClient) 939 | { 940 | mqttClient->connState = TCP_DISCONNECTING; 941 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); 942 | os_timer_disarm(&mqttClient->mqttTimer); 943 | } 944 | 945 | void ICACHE_FLASH_ATTR 946 | MQTT_DeleteClient(MQTT_Client *mqttClient) 947 | { 948 | if (NULL == mqttClient) 949 | return; 950 | 951 | mqttClient->connState = MQTT_DELETED; 952 | // if(TCP_DISCONNECTED == mqttClient->connState) { 953 | // mqttClient->connState = MQTT_DELETED; 954 | // } else if(MQTT_DELETED != mqttClient->connState) { 955 | // mqttClient->connState = MQTT_DELETING; 956 | // } 957 | 958 | system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); 959 | os_timer_disarm(&mqttClient->mqttTimer); 960 | } 961 | 962 | void ICACHE_FLASH_ATTR 963 | MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb) 964 | { 965 | mqttClient->connectedCb = connectedCb; 966 | } 967 | 968 | void ICACHE_FLASH_ATTR 969 | MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb) 970 | { 971 | mqttClient->disconnectedCb = disconnectedCb; 972 | } 973 | 974 | void ICACHE_FLASH_ATTR 975 | MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb) 976 | { 977 | mqttClient->dataCb = dataCb; 978 | } 979 | 980 | void ICACHE_FLASH_ATTR 981 | MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb) 982 | { 983 | mqttClient->publishedCb = publishedCb; 984 | } 985 | 986 | void ICACHE_FLASH_ATTR 987 | MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb) 988 | { 989 | mqttClient->timeoutCb = timeoutCb; 990 | } 991 | 992 | 993 | 994 | void ICACHE_FLASH_ATTR 995 | MQTT_SetUserId(MQTT_Client *mqttClient, const char* client_id) 996 | { 997 | if (mqttClient->connect_info.client_id != 0) { 998 | os_free(mqttClient->connect_info.client_id); 999 | mqttClient->connect_info.client_id = 0; 1000 | } 1001 | 1002 | 1003 | uint32_t len = os_strlen(client_id); 1004 | 1005 | mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(len + 1); 1006 | if (len) { 1007 | os_strcpy(mqttClient->connect_info.client_id, client_id); 1008 | } 1009 | mqttClient->connect_info.client_id[len] = 0; 1010 | } 1011 | 1012 | void ICACHE_FLASH_ATTR 1013 | MQTT_SetUserPwd(MQTT_Client *mqttClient, const char* user, const char* pwd) 1014 | { 1015 | uint32_t len; 1016 | 1017 | // free username 1018 | if (mqttClient->connect_info.username != 0) { 1019 | os_free(mqttClient->connect_info.username); 1020 | mqttClient->connect_info.username = 0; 1021 | } 1022 | // free password 1023 | if (mqttClient->connect_info.password != 0) { 1024 | os_free(mqttClient->connect_info.password); 1025 | mqttClient->connect_info.password = 0; 1026 | } 1027 | 1028 | 1029 | // copy username 1030 | len = os_strlen(user); 1031 | mqttClient->connect_info.username = (uint8_t*)os_zalloc(len + 1); 1032 | if (len) { 1033 | os_strcpy(mqttClient->connect_info.username, user); 1034 | } 1035 | mqttClient->connect_info.username[len] = 0; 1036 | 1037 | 1038 | // copy password 1039 | len = os_strlen(pwd); 1040 | mqttClient->connect_info.password = (uint8_t*)os_zalloc(len + 1); 1041 | if (len) { 1042 | os_strcpy(mqttClient->connect_info.password, pwd); 1043 | } 1044 | mqttClient->connect_info.password[len] = 0; 1045 | 1046 | } 1047 | 1048 | --------------------------------------------------------------------------------