From c60c8af237eb8cb3140a9a653c2eeb72505c926c Mon Sep 17 00:00:00 2001 From: Theo Arends Date: Fri, 2 Mar 2018 15:38:37 +0100 Subject: [PATCH] v5.12.0d - Add alternative MQTT libraries 5.12.0d * Add support for optional MQTT drivers to be selected in user_config.h (#1992) --- lib/TasmotaMqtt-1.1.0/.gitignore | 28 + lib/TasmotaMqtt-1.1.0/README.md | 8 + .../examples/mqtt_pub/mqtt_pub.ino | 102 ++ .../examples/mqtt_sub/mqtt_sub.ino | 97 ++ lib/TasmotaMqtt-1.1.0/keywords.txt | 39 + lib/TasmotaMqtt-1.1.0/library.properties | 9 + lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.cpp | 193 +++ lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.h | 88 ++ lib/TasmotaMqtt-1.1.0/src/mqtt/debug.h | 19 + lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.c | 997 ++++++++++++++++ lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.h | 148 +++ lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.c | 487 ++++++++ lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.h | 141 +++ lib/TasmotaMqtt-1.1.0/src/mqtt/proto.c | 129 ++ lib/TasmotaMqtt-1.1.0/src/mqtt/proto.h | 32 + lib/TasmotaMqtt-1.1.0/src/mqtt/queue.c | 75 ++ lib/TasmotaMqtt-1.1.0/src/mqtt/queue.h | 44 + lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.c | 67 ++ lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.h | 19 + lib/TasmotaMqtt-1.1.0/src/mqtt/typedef.h | 17 + lib/TasmotaMqtt-1.1.0/src/mqtt/user_config.h | 15 + lib/TasmotaMqtt-1.1.0/src/mqtt/utils.c | 149 +++ lib/TasmotaMqtt-1.1.0/src/mqtt/utils.h | 9 + lib/esp-mqtt-arduino-1.0.1.02.1/.gitignore | 28 + lib/esp-mqtt-arduino-1.0.1.02.1/README.md | 15 + .../examples/mqtt_pub/mqtt_pub.ino | 102 ++ .../examples/mqtt_sub/mqtt_sub.ino | 95 ++ lib/esp-mqtt-arduino-1.0.1.02.1/keywords.txt | 43 + .../library.properties | 9 + lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.cpp | 269 +++++ lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.h | 93 ++ .../src/mqtt/debug.h | 19 + .../src/mqtt/mqtt.c | 1048 +++++++++++++++++ .../src/mqtt/mqtt.h | 152 +++ .../src/mqtt/mqtt_config.h | 19 + .../src/mqtt/mqtt_msg.c | 487 ++++++++ .../src/mqtt/mqtt_msg.h | 141 +++ .../src/mqtt/proto.c | 129 ++ .../src/mqtt/proto.h | 32 + .../src/mqtt/queue.c | 75 ++ .../src/mqtt/queue.h | 44 + .../src/mqtt/ringbuf.c | 67 ++ .../src/mqtt/ringbuf.h | 19 + .../src/mqtt/typedef.h | 17 + .../src/mqtt/utils.c | 149 +++ .../src/mqtt/utils.h | 9 + sonoff/_releasenotes.ino | 2 +- sonoff/sonoff.ino | 259 ++-- sonoff/user_config.h | 14 +- sonoff/xdrv_00_mqtt.ino | 381 +++--- sonoff/xdrv_interface.ino | 5 +- sonoff/xsns_interface.ino | 4 +- 52 files changed, 6234 insertions(+), 404 deletions(-) create mode 100644 lib/TasmotaMqtt-1.1.0/.gitignore create mode 100644 lib/TasmotaMqtt-1.1.0/README.md create mode 100644 lib/TasmotaMqtt-1.1.0/examples/mqtt_pub/mqtt_pub.ino create mode 100644 lib/TasmotaMqtt-1.1.0/examples/mqtt_sub/mqtt_sub.ino create mode 100644 lib/TasmotaMqtt-1.1.0/keywords.txt create mode 100644 lib/TasmotaMqtt-1.1.0/library.properties create mode 100644 lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.cpp create mode 100644 lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.h create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/debug.h create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.c create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.h create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.c create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.h create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/proto.c create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/proto.h create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/queue.c create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/queue.h create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.c create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.h create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/typedef.h create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/user_config.h create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/utils.c create mode 100644 lib/TasmotaMqtt-1.1.0/src/mqtt/utils.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/.gitignore create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/README.md create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/examples/mqtt_pub/mqtt_pub.ino create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/examples/mqtt_sub/mqtt_sub.ino create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/keywords.txt create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/library.properties create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.cpp create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/debug.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt.c create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_config.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_msg.c create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_msg.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/proto.c create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/proto.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/queue.c create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/queue.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/ringbuf.c create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/ringbuf.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/typedef.h create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/utils.c create mode 100644 lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/utils.h diff --git a/lib/TasmotaMqtt-1.1.0/.gitignore b/lib/TasmotaMqtt-1.1.0/.gitignore new file mode 100644 index 000000000..2ee75414c --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/.gitignore @@ -0,0 +1,28 @@ +# C++ objects and libs + +*.slo +*.lo +*.o +#*.a +*.la +*.lai +*.so +*.dll +*.dylib + +#Makefile +*-build-* +build-* +*.autosave + +# .log files (usually created by QtTest - thanks to VestniK) +*.log + + +# Editors temporary files +*~ + + +#OSX +.DS_Store +._* diff --git a/lib/TasmotaMqtt-1.1.0/README.md b/lib/TasmotaMqtt-1.1.0/README.md new file mode 100644 index 000000000..db197299e --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/README.md @@ -0,0 +1,8 @@ +MQTT +==== + +A Wrapper around mqtt for Arduino to be used with esp8266 modules. + +It wraps a slightly modified version of mqtt for esp8266 ported by Tuan PM. +Original code for esp: https://github.com/tuanpmt/esp_mqtt +Original code for contiki: https://github.com/esar/contiki-mqtt diff --git a/lib/TasmotaMqtt-1.1.0/examples/mqtt_pub/mqtt_pub.ino b/lib/TasmotaMqtt-1.1.0/examples/mqtt_pub/mqtt_pub.ino new file mode 100644 index 000000000..17b3be8db --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/examples/mqtt_pub/mqtt_pub.ino @@ -0,0 +1,102 @@ +#include +#include + +void myDataCb(String& topic, String& data); +void myPublishedCb(); +void myDisconnectedCb(); +void myConnectedCb(); + +#define CLIENT_ID "client1" + +// create MQTT object +MQTT myMqtt(CLIENT_ID, "192.168.0.1", 1883); + +// +const char* ssid = "ssid"; +const char* password = "ssid_password"; + + +// +void setup() { + Serial.begin(115200); + delay(1000); + + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + Serial.println("Connecting to MQTT server"); + + // setup callbacks + myMqtt.onConnected(myConnectedCb); + myMqtt.onDisconnected(myDisconnectedCb); + myMqtt.onPublished(myPublishedCb); + myMqtt.onData(myDataCb); + + Serial.println("connect mqtt..."); + myMqtt.connect(); + + delay(10); +} + +// +void loop() { + + int value = analogRead(A0); + + String topic("/"); + topic += CLIENT_ID; + topic += "/value"; + + String valueStr(value); + + // publish value to topic + boolean result = myMqtt.publish(topic, valueStr); + + delay(1000); +} + + +/* + * + */ +void myConnectedCb() +{ + Serial.println("connected to MQTT server"); +} + +void myDisconnectedCb() +{ + Serial.println("disconnected. try to reconnect..."); + delay(500); + myMqtt.connect(); +} + +void myPublishedCb() +{ + //Serial.println("published."); +} + +void myDataCb(String& topic, String& data) +{ + + Serial.print(topic); + Serial.print(": "); + Serial.println(data); +} + + + diff --git a/lib/TasmotaMqtt-1.1.0/examples/mqtt_sub/mqtt_sub.ino b/lib/TasmotaMqtt-1.1.0/examples/mqtt_sub/mqtt_sub.ino new file mode 100644 index 000000000..e88e0a7bb --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/examples/mqtt_sub/mqtt_sub.ino @@ -0,0 +1,97 @@ +#include +#include + +// This needs testing + +void myDataCb(char* topic, uint8_t* data, unsigned int data_len); +void myPublishedCb(); +void myDisconnectedCb(); +void myConnectedCb(); + +#define CLIENT_ID "client3" +#define TOPIC "/client1/value" + +// create MQTT +TasmotaMqtt myMqtt(); + +const char* ssid = "ssid"; +const char* password = "ssid_password"; + +// +void setup() { + Serial.begin(115200); + delay(1000); + + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + + Serial.println("Connecting to MQTT server"); + + myMqtt.InitConnection("192.168.0.1", 1883); + myMqtt.InitClient(CLIENT_ID, "", ""); + myMqtt.InitLWT("/lwt", "offline"); + + // setup callbacks + myMqtt.OnConnected(myConnectedCb); + myMqtt.OnDisconnected(myDisconnectedCb); + myMqtt.OnPublished(myPublishedCb); + myMqtt.OnData(myDataCb); + + Serial.println("connect mqtt..."); + myMqtt.Connect(); + + Serial.println("subscribe to topic..."); + myMqtt.Subscribe(TOPIC); + + delay(10); +} + +// +void loop() { +} + + +/* + * + */ +void myConnectedCb() +{ + Serial.println("connected to MQTT server"); +} + +void myDisconnectedCb() +{ + Serial.println("disconnected. try to reconnect..."); + delay(500); + myMqtt.Connect(); +} + +void myPublishedCb() +{ + //Serial.println("published."); +} + +void myDataCb(char* topic, uint8_t* data, unsigned int data_len) +{ + Serial.print(topic); + Serial.print(": "); + Serial.println(data); +} + + + diff --git a/lib/TasmotaMqtt-1.1.0/keywords.txt b/lib/TasmotaMqtt-1.1.0/keywords.txt new file mode 100644 index 000000000..198919125 --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/keywords.txt @@ -0,0 +1,39 @@ +####################################### +# Syntax Coloring Map For Test +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +TasmotaMqtt.h KEYWORD1 +TasmotaMqtt KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +InitConnection KEYWORD2 +InitClient KEYWORD2 +InitLWT KEYWORD2 + +Connect KEYWORD2 +Disconnect KEYWORD2 +Connected KEYWORD2 + +Publish KEYWORD2 +Subscribe KEYWORD2 + +#general +OnConnected KEYWORD2 +OnDisconnected KEYWORD2 +OnData KEYWORD2 + +####################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/lib/TasmotaMqtt-1.1.0/library.properties b/lib/TasmotaMqtt-1.1.0/library.properties new file mode 100644 index 000000000..5a3802f35 --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/library.properties @@ -0,0 +1,9 @@ +name=TasmotaMqtt +version=1.0.0 +author=Theo Arends +maintainer=Theo Arends +sentence=A Wrapper around mqtt for Arduino to be used with esp8266 modules. +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 Original code for contiki: https://github.com/esar/contiki-mqtt +category=Communication +url= +architectures=esp8266 \ No newline at end of file diff --git a/lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.cpp b/lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.cpp new file mode 100644 index 000000000..91a49e0f6 --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.cpp @@ -0,0 +1,193 @@ +/* + TasmotaMqtt.cpp - Wrapper for mqtt for esp8266 by Tuan PM for Tasmota + + Copyright (C) 2018 Theo Arends and Ingo Randolf + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "TasmotaMqtt.h" + +#include "user_interface.h" +#include "osapi.h" +#include "os_type.h" + +/*********************************************************************************************\ + * Prerequisite + * + * Copy .c and .h files from https://github.com/tuanpmt/esp_mqtt folder mqtt to folder mqtt + * - Replace BOOL with bool + * - Remove variables certificate and private_key from file mqtt.c + * - Add file user_config.h with default defines for + * MQTT_BUF_SIZE 256, MQTT_RECONNECT_TIMEOUT 5, QUEUE_BUFFER_SIZE 2048 and PROTOCOL_NAMEv311 +\*********************************************************************************************/ + +/*********************************************************************************************\ + * Mqtt internal callbacks +\*********************************************************************************************/ + +static void mqttConnectedCb(uint32_t *args) +{ + MQTT_Client* client = (MQTT_Client*)args; + TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; + if (_this && _this->onMqttConnectedCb) _this->onMqttConnectedCb(); +} + +static void mqttDisconnectedCb(uint32_t *args) +{ + MQTT_Client* client = (MQTT_Client*)args; + TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; + if (_this && _this->onMqttDisconnectedCb) _this->onMqttDisconnectedCb(); +} + +static void mqttPublishedCb(uint32_t *args) +{ + MQTT_Client* client = (MQTT_Client*)args; + TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; + if (_this && _this->onMqttPublishedCb) _this->onMqttPublishedCb(); +} + +static void mqttTimeoutCb(uint32_t *args) +{ + MQTT_Client* client = (MQTT_Client*)args; + TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; + if (_this && _this->onMqttTimeoutCb) _this->onMqttTimeoutCb(); +} + +static void mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) +{ + MQTT_Client* client = (MQTT_Client*)args; + TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; + if (_this) _this->_onMqttDataCb(topic, topic_len, data, data_len); +} + +/*********************************************************************************************\ + * TasmotaMqtt class implementation +\*********************************************************************************************/ + +TasmotaMqtt::TasmotaMqtt() : + onMqttConnectedCb(0), + onMqttDisconnectedCb(0), + onMqttPublishedCb(0), + onMqttTimeoutCb(0), + onMqttDataCb(0) +{ +} + +TasmotaMqtt::~TasmotaMqtt() +{ + MQTT_DeleteClient(&mqttClient); +} + +void TasmotaMqtt::InitConnection(const char* host, uint32_t port, uint8_t security) +{ + MQTT_InitConnection(&mqttClient, (uint8_t*)host, port, security); + + // set user data + mqttClient.user_data = (void*)this; + + MQTT_OnConnected(&mqttClient, mqttConnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); + MQTT_OnPublished(&mqttClient, mqttPublishedCb); + MQTT_OnTimeout(&mqttClient, mqttTimeoutCb); + MQTT_OnData(&mqttClient, mqttDataCb); +} + +void TasmotaMqtt::InitClient(const char* client_id, const char* client_user, const char* client_pass, uint32_t keep_alive_time, uint8_t clean_session) +{ + MQTT_InitClient(&mqttClient, (uint8_t*)client_id, (uint8_t*)client_user, (uint8_t*)client_pass, keep_alive_time, clean_session); +} + +void TasmotaMqtt::DeleteClient() +{ + MQTT_DeleteClient(&mqttClient); +} + +void TasmotaMqtt::InitLWT(const char* will_topic, const char* will_msg, uint8_t will_qos, bool will_retain) +{ + MQTT_InitLWT(&mqttClient, (uint8_t*)will_topic, (uint8_t*)will_msg, will_qos, (uint8_t)will_retain); +} + +void TasmotaMqtt::OnConnected( void (*function)(void) ) +{ + onMqttConnectedCb = function; +} + +void TasmotaMqtt::OnDisconnected( void (*function)(void) ) +{ + onMqttDisconnectedCb = function; +} + +void TasmotaMqtt::OnPublished( void (*function)(void) ) +{ + onMqttPublishedCb = function; +} + +void TasmotaMqtt::OnTimeout( void (*function)(void) ) +{ + onMqttTimeoutCb = function; +} + +void TasmotaMqtt::OnData( void (*function)(char*, uint8_t*, unsigned int) ) +{ + onMqttDataCb = function; +} + +bool TasmotaMqtt::Subscribe(const char* topic, uint8_t qos) +{ + return MQTT_Subscribe(&mqttClient, (char*)topic, qos); +} + +bool TasmotaMqtt::Unsubscribe(const char* topic) +{ + return MQTT_UnSubscribe(&mqttClient, (char*)topic); +} + +void TasmotaMqtt::Connect() +{ + MQTT_Connect(&mqttClient); +} + +void TasmotaMqtt::Connect(const char* client_id, const char* client_user, const char* client_pass, const char* will_topic, const char* will_msg, uint8_t will_qos, bool will_retain) +{ + MQTT_InitClient(&mqttClient, (uint8_t*)client_id, (uint8_t*)client_user, (uint8_t*)client_pass, MQTT_KEEPALIVE, 1); + MQTT_InitLWT(&mqttClient, (uint8_t*)will_topic, (uint8_t*)will_msg, will_qos, (uint8_t)will_retain); + MQTT_Connect(&mqttClient); +} + +void TasmotaMqtt::Disconnect() +{ + MQTT_Disconnect(&mqttClient); +} + +bool TasmotaMqtt::Publish(const char* topic, const char* data, int data_length, int qos, bool retain) +{ + return MQTT_Publish(&mqttClient, topic, data, data_length, qos, (int)retain); +} + +bool TasmotaMqtt::Connected() +{ + return (mqttClient.connState > TCP_CONNECTED); +} + +/*********************************************************************************************/ + +void TasmotaMqtt::_onMqttDataCb(const char* topic, uint32_t topic_len, const char* data, uint32_t data_len) +{ + char topic_copy[topic_len]; + + memcpy(topic_copy, topic, topic_len); + topic_copy[topic_len] = 0; + onMqttDataCb((char*)topic_copy, (byte*)data, data_len); +} diff --git a/lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.h b/lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.h new file mode 100644 index 000000000..e512d8d5a --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/TasmotaMqtt.h @@ -0,0 +1,88 @@ +/* + TasmotaMqtt.h - Wrapper for mqtt for esp8266 by Tuan PM for Tasmota + + Copyright (C) 2018 Theo Arends and Ingo Randolf + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef TasmotaMqtt_h +#define TasmotaMqtt_h +/*********************************************************************************************\ + * TasmotaMqtt supports currently only non-TLS MQTT + * + * Adapted from esp-mqtt-arduino by Ingo Randolf (https://github.com/i-n-g-o/esp-mqtt-arduino) +\*********************************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +extern "C" { + #include + #include "mqtt/mqtt.h" +} + +// MQTT_KEEPALIVE : keepAlive interval in Seconds +#ifndef MQTT_KEEPALIVE +#define MQTT_KEEPALIVE 15 +#endif + +class TasmotaMqtt { +public: + TasmotaMqtt(); + ~TasmotaMqtt(); + + void InitConnection(const char* host, uint32_t port, uint8_t security = 0); + void InitClient(const char* client_id, const char* client_user, const char* client_pass, uint32_t keep_alive_time = MQTT_KEEPALIVE, uint8_t clean_session = 1); + void DeleteClient(); + void InitLWT(const char* will_topic, const char* will_msg, uint8_t will_qos = 0, bool will_retain = false); + + void OnConnected( void (*)(void) ); + void OnDisconnected( void (*)(void) ); + void OnPublished( void (*)(void) ); + void OnTimeout( void (*)(void) ); + void OnData( void (*)(char*, uint8_t*, unsigned int) ); + + bool Subscribe(const char* topic, uint8_t qos = 0); + bool Unsubscribe(const char* topic); + + void Connect(); + void Connect(const char* client_id, const char* client_user, const char* client_pass, const char* will_topic, const char* will_msg, uint8_t will_qos = 0, bool will_retain = false); + void Disconnect(); + + bool Publish(const char* topic, const char* data, int data_length, int qos = 0, bool retain = false); + + bool Connected(); + + int State() { return mqttClient.connState; }; + + void (*onMqttConnectedCb)(void); + void (*onMqttDisconnectedCb)(void); + void (*onMqttPublishedCb)(void); + void (*onMqttTimeoutCb)(void); + void (*onMqttDataCb) (char*, uint8_t*, unsigned int); + + // internal callback + void _onMqttDataCb(const char*, uint32_t, const char*, uint32_t); + +private: + MQTT_Client mqttClient; +}; + +#endif // TasmotaMqtt_h \ No newline at end of file diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/debug.h b/lib/TasmotaMqtt-1.1.0/src/mqtt/debug.h new file mode 100644 index 000000000..f45dd6d8d --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/debug.h @@ -0,0 +1,19 @@ +/* + * debug.h + * + * Created on: Dec 4, 2014 + * Author: Minh + */ + +#ifndef USER_DEBUG_H_ +#define USER_DEBUG_H_ + + +#if defined(MQTT_DEBUG_ON) +#define MQTT_INFO( format, ... ) os_printf( format, ## __VA_ARGS__ ) +#else +#define MQTT_INFO( format, ... ) +#endif + + +#endif /* USER_DEBUG_H_ */ diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.c b/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.c new file mode 100644 index 000000000..06609fe4a --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.c @@ -0,0 +1,997 @@ +/* mqtt.c +* Protocol: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Redis nor the names of its contributors may be used +* to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "user_interface.h" +#include "osapi.h" +#include "espconn.h" +#include "os_type.h" +#include "mem.h" +#include "mqtt_msg.h" +#include "debug.h" +#include "user_config.h" +#include "mqtt.h" +#include "queue.h" + +#define MQTT_TASK_PRIO 2 +#define MQTT_TASK_QUEUE_SIZE 1 +#define MQTT_SEND_TIMOUT 5 + +#ifndef MQTT_SSL_SIZE +#define MQTT_SSL_SIZE 5120 +#endif + +#ifndef QUEUE_BUFFER_SIZE +#define QUEUE_BUFFER_SIZE 2048 +#endif + +/* +unsigned char *default_certificate; +unsigned int default_certificate_len = 0; +unsigned char *default_private_key; +unsigned int default_private_key_len = 0; +*/ + +os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; + +#ifdef PROTOCOL_NAMEv311 +LOCAL uint8_t zero_len_id[2] = { 0, 0 }; +#endif + +LOCAL void ICACHE_FLASH_ATTR +mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + struct espconn *pConn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pConn->reverse; + + + if (ipaddr == NULL) + { + MQTT_INFO("DNS: Found, but got no ip, try to reconnect\r\n"); + client->connState = TCP_RECONNECT_REQ; + return; + } + + MQTT_INFO("DNS: found ip %d.%d.%d.%d\n", + *((uint8 *) &ipaddr->addr), + *((uint8 *) &ipaddr->addr + 1), + *((uint8 *) &ipaddr->addr + 2), + *((uint8 *) &ipaddr->addr + 3)); + + if (client->ip.addr == 0 && ipaddr->addr != 0) + { + os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE); + espconn_secure_connect(client->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_connect(client->pCon); + } + + client->connState = TCP_CONNECTING; + MQTT_INFO("TCP: connecting...\r\n"); + } + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + + + +LOCAL void ICACHE_FLASH_ATTR +deliver_publish(MQTT_Client* client, uint8_t* message, int length) +{ + mqtt_event_data_t event_data; + + event_data.topic_length = length; + event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); + event_data.data_length = length; + event_data.data = mqtt_get_publish_data(message, &event_data.data_length); + + if (client->dataCb) + client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); + +} + +void ICACHE_FLASH_ATTR +mqtt_send_keepalive(MQTT_Client *client) +{ + MQTT_INFO("\r\nMQTT: Send keepalive packet to %s:%d!\r\n", client->host, client->port); + client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); + client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ; + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); + err_t result = ESPCONN_OK; + if (client->security) { +#ifdef MQTT_SSL_ENABLE + result = espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + result = espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + } + + client->mqtt_state.outbound_message = NULL; + if (ESPCONN_OK == result) { + client->keepAliveTick = 0; + client->connState = MQTT_DATA; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } + else { + client->connState = TCP_RECONNECT_DISCONNECTING; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } +} + +/** + * @brief Delete tcp client and free all memory + * @param mqttClient: The mqtt client which contain TCP client + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_delete(MQTT_Client *mqttClient) +{ + if (mqttClient->pCon != NULL) { + MQTT_INFO("TCP: Free memory\r\n"); + // Force abort connections + espconn_abort(mqttClient->pCon); + // Delete connections + espconn_delete(mqttClient->pCon); + + if (mqttClient->pCon->proto.tcp) { + os_free(mqttClient->pCon->proto.tcp); + mqttClient->pCon->proto.tcp = NULL; + } + os_free(mqttClient->pCon); + mqttClient->pCon = NULL; + } +} + +/** + * @brief Delete MQTT client and free all memory + * @param mqttClient: The mqtt client + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_client_delete(MQTT_Client *mqttClient) +{ + if (mqttClient == NULL) + return; + + if (mqttClient->pCon != NULL) { + mqtt_tcpclient_delete(mqttClient); + } + + if (mqttClient->host != NULL) { + os_free(mqttClient->host); + mqttClient->host = NULL; + } + + if (mqttClient->user_data != NULL) { + os_free(mqttClient->user_data); + mqttClient->user_data = NULL; + } + + if (mqttClient->mqtt_state.in_buffer != NULL) { + os_free(mqttClient->mqtt_state.in_buffer); + mqttClient->mqtt_state.in_buffer = NULL; + } + + if (mqttClient->mqtt_state.out_buffer != NULL) { + os_free(mqttClient->mqtt_state.out_buffer); + mqttClient->mqtt_state.out_buffer = NULL; + } + + if (mqttClient->mqtt_state.outbound_message != NULL) { + if (mqttClient->mqtt_state.outbound_message->data != NULL) + { + os_free(mqttClient->mqtt_state.outbound_message->data); + mqttClient->mqtt_state.outbound_message->data = NULL; + } + } + + if (mqttClient->mqtt_state.mqtt_connection.buffer != NULL) { + // Already freed but not NULL + mqttClient->mqtt_state.mqtt_connection.buffer = NULL; + } + + if (mqttClient->connect_info.client_id != NULL) { +#ifdef PROTOCOL_NAMEv311 + /* Don't attempt to free if it's the zero_len array */ + if ( ((uint8_t*)mqttClient->connect_info.client_id) != zero_len_id ) + os_free(mqttClient->connect_info.client_id); +#else + os_free(mqttClient->connect_info.client_id); +#endif + mqttClient->connect_info.client_id = NULL; + } + + if (mqttClient->connect_info.username != NULL) { + os_free(mqttClient->connect_info.username); + mqttClient->connect_info.username = NULL; + } + + if (mqttClient->connect_info.password != NULL) { + os_free(mqttClient->connect_info.password); + mqttClient->connect_info.password = NULL; + } + + if (mqttClient->connect_info.will_topic != NULL) { + os_free(mqttClient->connect_info.will_topic); + mqttClient->connect_info.will_topic = NULL; + } + + if (mqttClient->connect_info.will_message != NULL) { + os_free(mqttClient->connect_info.will_message); + mqttClient->connect_info.will_message = NULL; + } + + if (mqttClient->msgQueue.buf != NULL) { + os_free(mqttClient->msgQueue.buf); + mqttClient->msgQueue.buf = NULL; + } + + // Initialize state + mqttClient->connState = WIFI_INIT; + // Clear callback functions to avoid abnormal callback + mqttClient->connectedCb = NULL; + mqttClient->disconnectedCb = NULL; + mqttClient->publishedCb = NULL; + mqttClient->timeoutCb = NULL; + mqttClient->dataCb = NULL; + + MQTT_INFO("MQTT: client already deleted\r\n"); +} + + +/** + * @brief Client received callback function. + * @param arg: contain the ip link information + * @param pdata: received data + * @param len: the lenght of received data + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len) +{ + uint8_t msg_type; + uint8_t msg_qos; + uint16_t msg_id; + uint8_t msg_conn_ret; + + struct espconn *pCon = (struct espconn*)arg; + MQTT_Client *client = (MQTT_Client *)pCon->reverse; + +READPACKET: + MQTT_INFO("TCP: data received %d bytes\r\n", len); + // MQTT_INFO("STATE: %d\r\n", client->connState); + if (len < MQTT_BUF_SIZE && len > 0) { + os_memcpy(client->mqtt_state.in_buffer, pdata, len); + + msg_type = mqtt_get_type(client->mqtt_state.in_buffer); + msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); + msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); + switch (client->connState) { + case MQTT_CONNECT_SENDING: + if (msg_type == MQTT_MSG_TYPE_CONNACK) { + if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT) { + MQTT_INFO("MQTT: Invalid packet\r\n"); + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_disconnect(client->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_disconnect(client->pCon); + } + } else { + msg_conn_ret = mqtt_get_connect_return_code(client->mqtt_state.in_buffer); + switch (msg_conn_ret) { + case CONNECTION_ACCEPTED: + MQTT_INFO("MQTT: Connected to %s:%d\r\n", client->host, client->port); + client->connState = MQTT_DATA; + if (client->connectedCb) + client->connectedCb((uint32_t*)client); + break; + case CONNECTION_REFUSE_PROTOCOL: + case CONNECTION_REFUSE_SERVER_UNAVAILABLE: + case CONNECTION_REFUSE_BAD_USERNAME: + case CONNECTION_REFUSE_NOT_AUTHORIZED: + MQTT_INFO("MQTT: Connection refuse, reason code: %d\r\n", msg_conn_ret); + default: + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_disconnect(client->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_disconnect(client->pCon); + } + + } + + } + + } + break; + case MQTT_DATA: + case MQTT_KEEPALIVE_SEND: + client->mqtt_state.message_length_read = len; + client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + + + switch (msg_type) + { + + case MQTT_MSG_TYPE_SUBACK: + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) + MQTT_INFO("MQTT: Subscribe successful\r\n"); + break; + case MQTT_MSG_TYPE_UNSUBACK: + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) + MQTT_INFO("MQTT: UnSubscribe successful\r\n"); + break; + case MQTT_MSG_TYPE_PUBLISH: + if (msg_qos == 1) + client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); + else if (msg_qos == 2) + client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); + if (msg_qos == 1 || msg_qos == 2) { + MQTT_INFO("MQTT: Queue response QoS: %d\r\n", msg_qos); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + } + } + + deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + break; + case MQTT_MSG_TYPE_PUBACK: + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { + MQTT_INFO("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\r\n"); + } + + break; + case MQTT_MSG_TYPE_PUBREC: + client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + } + break; + case MQTT_MSG_TYPE_PUBREL: + client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + } + break; + case MQTT_MSG_TYPE_PUBCOMP: + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { + MQTT_INFO("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\r\n"); + } + break; + case MQTT_MSG_TYPE_PINGREQ: + client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + } + break; + case MQTT_MSG_TYPE_PINGRESP: + // Ignore + break; + } + // NOTE: this is done down here and not in the switch case above + // because the PSOCK_READBUF_LEN() won't work inside a switch + // statement due to the way protothreads resume. + if (msg_type == MQTT_MSG_TYPE_PUBLISH) + { + len = client->mqtt_state.message_length_read; + + if (client->mqtt_state.message_length < client->mqtt_state.message_length_read) + { + //client->connState = MQTT_PUBLISH_RECV; + //Not Implement yet + len -= client->mqtt_state.message_length; + pdata += client->mqtt_state.message_length; + + MQTT_INFO("Get another published message\r\n"); + goto READPACKET; + } + + } + break; + } + } else { + MQTT_INFO("ERROR: Message too long\r\n"); + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** + * @brief Client send over callback function. + * @param arg: contain the ip link information + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_sent_cb(void *arg) +{ + struct espconn *pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + MQTT_INFO("TCP: Sent\r\n"); + client->sendTimeout = 0; + client->keepAliveTick = 0; + + if ((client->connState == MQTT_DATA || client->connState == MQTT_KEEPALIVE_SEND) + && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH) { + if (client->publishedCb) + client->publishedCb((uint32_t*)client); + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +void ICACHE_FLASH_ATTR mqtt_timer(void *arg) +{ + MQTT_Client* client = (MQTT_Client*)arg; + + if (client->connState == MQTT_DATA) { + client->keepAliveTick ++; + if (client->keepAliveTick > (client->mqtt_state.connect_info->keepalive / 2)) { + client->connState = MQTT_KEEPALIVE_SEND; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } + + } else if (client->connState == TCP_RECONNECT_REQ) { + client->reconnectTick ++; + if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { + client->reconnectTick = 0; + client->connState = TCP_RECONNECT; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + if (client->timeoutCb) + client->timeoutCb((uint32_t*)client); + } + } + if (client->sendTimeout > 0) + client->sendTimeout --; +} + +void ICACHE_FLASH_ATTR +mqtt_tcpclient_discon_cb(void *arg) +{ + + struct espconn *pespconn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pespconn->reverse; + MQTT_INFO("TCP: Disconnected callback\r\n"); + if (TCP_DISCONNECTING == client->connState) { + client->connState = TCP_DISCONNECTED; + } + else if (MQTT_DELETING == client->connState) { + client->connState = MQTT_DELETED; + } + else { + client->connState = TCP_RECONNECT_REQ; + } + if (client->disconnectedCb) + client->disconnectedCb((uint32_t*)client); + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + + + +/** + * @brief Tcp client connect success callback function. + * @param arg: contain the ip link information + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_connect_cb(void *arg) +{ + struct espconn *pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + + espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); + espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);//////// + espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);/////// + MQTT_INFO("MQTT: Connected to broker %s:%d\r\n", client->host, client->port); + + mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); + client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + } + + client->mqtt_state.outbound_message = NULL; + client->connState = MQTT_CONNECT_SENDING; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** + * @brief Tcp client connect repeat callback function. + * @param arg: contain the ip link information + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_recon_cb(void *arg, sint8 errType) +{ + struct espconn *pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + + MQTT_INFO("TCP: Reconnect to %s:%d\r\n", client->host, client->port); + + client->connState = TCP_RECONNECT_REQ; + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + +} + +/** + * @brief MQTT publish function. + * @param client: MQTT_Client reference + * @param topic: string topic will publish to + * @param data: buffer data send point to + * @param data_length: length of data + * @param qos: qos + * @param retain: retain + * @retval TRUE if success queue + */ +bool ICACHE_FLASH_ATTR +MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain) +{ + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, + topic, data, data_length, + qos, retain, + &client->mqtt_state.pending_msg_id); + if (client->mqtt_state.outbound_message->length == 0) { + MQTT_INFO("MQTT: Queuing publish failed\r\n"); + return FALSE; + } + 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); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + MQTT_INFO("MQTT: Serious buffer error\r\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +/** + * @brief MQTT subscibe function. + * @param client: MQTT_Client reference + * @param topic: string topic will subscribe + * @param qos: qos + * @retval TRUE if success queue + */ +bool ICACHE_FLASH_ATTR +MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos) +{ + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + + client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, + topic, qos, + &client->mqtt_state.pending_msg_id); + MQTT_INFO("MQTT: queue subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + MQTT_INFO("MQTT: Serious buffer error\r\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + + return TRUE; +} + +/** + * @brief MQTT un-subscibe function. + * @param client: MQTT_Client reference + * @param topic: String topic will un-subscribe + * @retval TRUE if success queue + */ +bool ICACHE_FLASH_ATTR +MQTT_UnSubscribe(MQTT_Client *client, char* topic) +{ + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection, + topic, + &client->mqtt_state.pending_msg_id); + MQTT_INFO("MQTT: queue un-subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + MQTT_INFO("MQTT: Serious buffer error\r\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +/** + * @brief MQTT ping function. + * @param client: MQTT_Client reference + * @retval TRUE if success queue + */ +bool ICACHE_FLASH_ATTR +MQTT_Ping(MQTT_Client *client) +{ + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); + if (client->mqtt_state.outbound_message->length == 0) { + MQTT_INFO("MQTT: Queuing publish failed\r\n"); + return FALSE; + } + 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); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + MQTT_INFO("MQTT: Serious buffer error\r\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +void ICACHE_FLASH_ATTR +MQTT_Task(os_event_t *e) +{ + MQTT_Client* client = (MQTT_Client*)e->par; + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + if (e->par == 0) + return; + switch (client->connState) { + + case TCP_RECONNECT_REQ: + break; + case TCP_RECONNECT: + mqtt_tcpclient_delete(client); + MQTT_Connect(client); + MQTT_INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port); + client->connState = TCP_CONNECTING; + break; + case MQTT_DELETING: + case TCP_DISCONNECTING: + case TCP_RECONNECT_DISCONNECTING: + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_disconnect(client->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_disconnect(client->pCon); + } + break; + case TCP_DISCONNECTED: + MQTT_INFO("MQTT: Disconnected\r\n"); + mqtt_tcpclient_delete(client); + break; + case MQTT_DELETED: + MQTT_INFO("MQTT: Deleted client\r\n"); + mqtt_client_delete(client); + break; + case MQTT_KEEPALIVE_SEND: + mqtt_send_keepalive(client); + break; + case MQTT_DATA: + if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) { + break; + } + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0) { + client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); + client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); + client->keepAliveTick = 0; + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_send(client->pCon, dataBuffer, dataLen); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_send(client->pCon, dataBuffer, dataLen); + } + + client->mqtt_state.outbound_message = NULL; + break; + } + break; + } +} + +/** + * @brief MQTT initialization connection function + * @param client: MQTT_Client reference + * @param host: Domain or IP string + * @param port: Port to connect + * @param security: 1 for ssl, 0 for none + * @retval None + */ +void ICACHE_FLASH_ATTR +MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security) +{ + uint32_t temp; + MQTT_INFO("MQTT:InitConnection\r\n"); + os_memset(mqttClient, 0, sizeof(MQTT_Client)); + temp = os_strlen(host); + mqttClient->host = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->host, host); + mqttClient->host[temp] = 0; + mqttClient->port = port; + mqttClient->security = security; + +} + +/** + * @brief MQTT initialization mqtt client function + * @param client: MQTT_Client reference + * @param clientid: MQTT client id + * @param client_user:MQTT client user + * @param client_pass:MQTT client password + * @param client_pass:MQTT keep alive timer, in second + * @retval None + */ +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) +{ + uint32_t temp; + MQTT_INFO("MQTT:InitClient\r\n"); + + os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); + + if ( !client_id ) + { + /* Should be allowed by broker, but clean session flag must be set. */ + #ifdef PROTOCOL_NAMEv311 + if (cleanSession) + { + mqttClient->connect_info.client_id = zero_len_id; + } else { + MQTT_INFO("cleanSession must be set to use 0 length client_id\r\n"); + return false; + } + /* Not supported. Return. */ + #else + MQTT_INFO("Client ID required for MQTT < 3.1.1!\r\n"); + return false; + #endif + } + + /* If connect_info's client_id is still NULL and we get here, we can * + * assume the passed client_id is non-NULL. */ + if ( !(mqttClient->connect_info.client_id) ) + { + temp = os_strlen(client_id); + mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.client_id, client_id); + mqttClient->connect_info.client_id[temp] = 0; + } + + if (client_user) + { + temp = os_strlen(client_user); + mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.username, client_user); + mqttClient->connect_info.username[temp] = 0; + } + + if (client_pass) + { + temp = os_strlen(client_pass); + mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.password, client_pass); + mqttClient->connect_info.password[temp] = 0; + } + + + mqttClient->connect_info.keepalive = keepAliveTime; + mqttClient->connect_info.clean_session = cleanSession; + + mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); + mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; + mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); + mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; + mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; + + mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length); + + QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); + + system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); + return true; +} +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) +{ + uint32_t temp; + temp = os_strlen(will_topic); + mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.will_topic, will_topic); + mqttClient->connect_info.will_topic[temp] = 0; + + temp = os_strlen(will_msg); + mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.will_message, will_msg); + mqttClient->connect_info.will_message[temp] = 0; + + + mqttClient->connect_info.will_qos = will_qos; + mqttClient->connect_info.will_retain = will_retain; +} +/** + * @brief Begin connect to MQTT broker + * @param client: MQTT_Client reference + * @retval None + */ +void ICACHE_FLASH_ATTR +MQTT_Connect(MQTT_Client *mqttClient) +{ + if (mqttClient->pCon) { + // Clean up the old connection forcefully - using MQTT_Disconnect + // does not actually release the old connection until the + // disconnection callback is invoked. + mqtt_tcpclient_delete(mqttClient); + } + mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); + mqttClient->pCon->type = ESPCONN_TCP; + mqttClient->pCon->state = ESPCONN_NONE; + mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); + mqttClient->pCon->proto.tcp->local_port = espconn_port(); + mqttClient->pCon->proto.tcp->remote_port = mqttClient->port; + mqttClient->pCon->reverse = mqttClient; + espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); + espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); + + mqttClient->keepAliveTick = 0; + mqttClient->reconnectTick = 0; + + + os_timer_disarm(&mqttClient->mqttTimer); + os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); + os_timer_arm(&mqttClient->mqttTimer, 1000, 1); + + if (UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip)) { + MQTT_INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port); + if (mqttClient->security) + { +#ifdef MQTT_SSL_ENABLE + espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE); + espconn_secure_connect(mqttClient->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else + { + espconn_connect(mqttClient->pCon); + } + } + else { + MQTT_INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port); + espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found); + } + mqttClient->connState = TCP_CONNECTING; +} + +void ICACHE_FLASH_ATTR +MQTT_Disconnect(MQTT_Client *mqttClient) +{ + mqttClient->connState = TCP_DISCONNECTING; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); + os_timer_disarm(&mqttClient->mqttTimer); +} + +void ICACHE_FLASH_ATTR +MQTT_DeleteClient(MQTT_Client *mqttClient) +{ + if (NULL == mqttClient) + return; + + mqttClient->connState = MQTT_DELETED; + // if(TCP_DISCONNECTED == mqttClient->connState) { + // mqttClient->connState = MQTT_DELETED; + // } else if(MQTT_DELETED != mqttClient->connState) { + // mqttClient->connState = MQTT_DELETING; + // } + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); + os_timer_disarm(&mqttClient->mqttTimer); +} + +void ICACHE_FLASH_ATTR +MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb) +{ + mqttClient->connectedCb = connectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb) +{ + mqttClient->disconnectedCb = disconnectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb) +{ + mqttClient->dataCb = dataCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb) +{ + mqttClient->publishedCb = publishedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb) +{ + mqttClient->timeoutCb = timeoutCb; +} diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.h b/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.h new file mode 100644 index 000000000..96489107e --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt.h @@ -0,0 +1,148 @@ +/* mqtt.h +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Redis nor the names of its contributors may be used +* to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef USER_AT_MQTT_H_ +#define USER_AT_MQTT_H_ +#include "user_config.h" +#include "mqtt_msg.h" +#include "user_interface.h" + +#include "queue.h" +typedef struct mqtt_event_data_t +{ + uint8_t type; + const char* topic; + const char* data; + uint16_t topic_length; + uint16_t data_length; + uint16_t data_offset; +} mqtt_event_data_t; + +typedef struct mqtt_state_t +{ + uint16_t port; + int auto_reconnect; + mqtt_connect_info_t* connect_info; + uint8_t* in_buffer; + uint8_t* out_buffer; + int in_buffer_length; + int out_buffer_length; + uint16_t message_length; + uint16_t message_length_read; + mqtt_message_t* outbound_message; + mqtt_connection_t mqtt_connection; + uint16_t pending_msg_id; + int pending_msg_type; + int pending_publish_qos; +} mqtt_state_t; + +typedef enum { + WIFI_INIT, + WIFI_CONNECTING, + WIFI_CONNECTING_ERROR, + WIFI_CONNECTED, + DNS_RESOLVE, + TCP_DISCONNECTING, + TCP_DISCONNECTED, + TCP_RECONNECT_DISCONNECTING, + TCP_RECONNECT_REQ, + TCP_RECONNECT, + TCP_CONNECTING, + TCP_CONNECTING_ERROR, + TCP_CONNECTED, + MQTT_CONNECT_SEND, + MQTT_CONNECT_SENDING, + MQTT_SUBSCIBE_SEND, + MQTT_SUBSCIBE_SENDING, + MQTT_DATA, + MQTT_KEEPALIVE_SEND, + MQTT_PUBLISH_RECV, + MQTT_PUBLISHING, + MQTT_DELETING, + MQTT_DELETED, +} tConnState; + +typedef void (*MqttCallback)(uint32_t *args); +typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh); + +typedef struct { + struct espconn *pCon; + uint8_t security; + uint8_t* host; + uint32_t port; + ip_addr_t ip; + mqtt_state_t mqtt_state; + mqtt_connect_info_t connect_info; + MqttCallback connectedCb; + MqttCallback disconnectedCb; + MqttCallback publishedCb; + MqttCallback timeoutCb; + MqttDataCallback dataCb; + ETSTimer mqttTimer; + uint32_t keepAliveTick; + uint32_t reconnectTick; + uint32_t sendTimeout; + tConnState connState; + QUEUE msgQueue; + void* user_data; +} MQTT_Client; + +#define SEC_NONSSL 0 +#define SEC_SSL 1 + +#define MQTT_FLAG_CONNECTED 1 +#define MQTT_FLAG_READY 2 +#define MQTT_FLAG_EXIT 4 + +#define MQTT_EVENT_TYPE_NONE 0 +#define MQTT_EVENT_TYPE_CONNECTED 1 +#define MQTT_EVENT_TYPE_DISCONNECTED 2 +#define MQTT_EVENT_TYPE_SUBSCRIBED 3 +#define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 +#define MQTT_EVENT_TYPE_PUBLISH 5 +#define MQTT_EVENT_TYPE_PUBLISHED 6 +#define MQTT_EVENT_TYPE_EXITED 7 +#define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 + +void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security); +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); +void ICACHE_FLASH_ATTR MQTT_DeleteClient(MQTT_Client *mqttClient); +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); +void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb); +void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb); +void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb); +void ICACHE_FLASH_ATTR MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb); +void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb); +bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos); +bool ICACHE_FLASH_ATTR MQTT_UnSubscribe(MQTT_Client *client, char* topic); +void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient); +void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient); +bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain); + +#endif /* USER_AT_MQTT_H_ */ diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.c b/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.c new file mode 100644 index 000000000..57dcbac27 --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.c @@ -0,0 +1,487 @@ +/* +* Copyright (c) 2014, Stephen Robinson +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include +#include "mqtt_msg.h" +#include "user_config.h" +#define MQTT_MAX_FIXED_HEADER_SIZE 3 + +enum mqtt_connect_flag +{ + MQTT_CONNECT_FLAG_USERNAME = 1 << 7, + MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, + MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, + MQTT_CONNECT_FLAG_WILL = 1 << 2, + MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 +}; + +struct __attribute((__packed__)) mqtt_connect_variable_header +{ + uint8_t lengthMsb; + uint8_t lengthLsb; +#if defined(PROTOCOL_NAMEv31) + uint8_t magic[6]; +#elif defined(PROTOCOL_NAMEv311) + uint8_t magic[4]; +#else +#error "Please define protocol name" +#endif + uint8_t version; + uint8_t flags; + uint8_t keepaliveMsb; + uint8_t keepaliveLsb; +}; + +static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len) +{ + if (connection->message.length + len + 2 > connection->buffer_length) + return -1; + + connection->buffer[connection->message.length++] = len >> 8; + connection->buffer[connection->message.length++] = len & 0xff; + memcpy(connection->buffer + connection->message.length, string, len); + connection->message.length += len; + + return len + 2; +} + +static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id) +{ + // If message_id is zero then we should assign one, otherwise + // we'll use the one supplied by the caller + while (message_id == 0) + message_id = ++connection->message_id; + + if (connection->message.length + 2 > connection->buffer_length) + return 0; + + connection->buffer[connection->message.length++] = message_id >> 8; + connection->buffer[connection->message.length++] = message_id & 0xff; + + return message_id; +} + +static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection) +{ + connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; + return MQTT_MAX_FIXED_HEADER_SIZE; +} + +static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection) +{ + connection->message.data = connection->buffer; + connection->message.length = 0; + return &connection->message; +} + +static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) +{ + int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; + + if (remaining_length > 127) + { + connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[1] = 0x80 | (remaining_length % 128); + connection->buffer[2] = remaining_length / 128; + connection->message.length = remaining_length + 3; + connection->message.data = connection->buffer; + } + else + { + connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[2] = remaining_length; + connection->message.length = remaining_length + 2; + connection->message.data = connection->buffer + 1; + } + + return &connection->message; +} + +void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) +{ + memset(connection, 0, sizeof(mqtt_connection_t)); + connection->buffer = buffer; + connection->buffer_length = buffer_length; +} + +int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length) +{ + int i; + int totlen = 0; + + for (i = 1; i < length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + return totlen; +} + +const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) +{ + int i; + int totlen = 0; + int topiclen; + + for (i = 1; i < *length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + if (i + 2 >= *length) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen > *length) + return NULL; + + *length = topiclen; + return (const char*)(buffer + i); +} + +const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) +{ + int i; + int totlen = 0; + int topiclen; + int blength = *length; + *length = 0; + + for (i = 1; i < blength; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + if (i + 2 >= blength) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen >= blength) + return NULL; + + i += topiclen; + + if (mqtt_get_qos(buffer) > 0) + { + if (i + 2 >= blength) + return NULL; + i += 2; + } + + if (totlen < i) + return NULL; + + if (totlen <= blength) + *length = totlen - i; + else + *length = blength - i; + return (const char*)(buffer + i); +} + +uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length) +{ + if (length < 1) + return 0; + + switch (mqtt_get_type(buffer)) + { + case MQTT_MSG_TYPE_PUBLISH: + { + int i; + int topiclen; + + for (i = 1; i < length; ++i) + { + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + + if (i + 2 >= length) + return 0; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen >= length) + return 0; + i += topiclen; + + if (mqtt_get_qos(buffer) > 0) + { + if (i + 2 >= length) + return 0; + //i += 2; + } else { + return 0; + } + + return (buffer[i] << 8) | buffer[i + 1]; + } + case MQTT_MSG_TYPE_PUBACK: + case MQTT_MSG_TYPE_PUBREC: + case MQTT_MSG_TYPE_PUBREL: + case MQTT_MSG_TYPE_PUBCOMP: + case MQTT_MSG_TYPE_SUBACK: + case MQTT_MSG_TYPE_UNSUBACK: + case MQTT_MSG_TYPE_SUBSCRIBE: + { + // This requires the remaining length to be encoded in 1 byte, + // which it should be. + if (length >= 4 && (buffer[1] & 0x80) == 0) + return (buffer[2] << 8) | buffer[3]; + else + return 0; + } + + default: + return 0; + } +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) +{ + struct mqtt_connect_variable_header* variable_header; + + init_message(connection); + + if (connection->message.length + sizeof(*variable_header) > connection->buffer_length) + return fail_message(connection); + variable_header = (void*)(connection->buffer + connection->message.length); + connection->message.length += sizeof(*variable_header); + + variable_header->lengthMsb = 0; +#if defined(PROTOCOL_NAMEv31) + variable_header->lengthLsb = 6; + memcpy(variable_header->magic, "MQIsdp", 6); + variable_header->version = 3; +#elif defined(PROTOCOL_NAMEv311) + variable_header->lengthLsb = 4; + memcpy(variable_header->magic, "MQTT", 4); + variable_header->version = 4; +#else +#error "Please define protocol name" +#endif + + variable_header->flags = 0; + variable_header->keepaliveMsb = info->keepalive >> 8; + variable_header->keepaliveLsb = info->keepalive & 0xff; + + if (info->clean_session) + variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; + + if (info->client_id == NULL) + { + /* Never allowed */ + return fail_message(connection); + } + else if (info->client_id[0] == '\0') + { +#ifdef PROTOCOL_NAMEv311 + /* Allowed. Format 0 Length ID */ + append_string(connection, info->client_id, 2) ; +#else + /* 0 Length not allowed */ + return fail_message(connection); +#endif + } + else + { + /* No 0 data and at least 1 long. Good to go. */ + if(append_string(connection, info->client_id, strlen(info->client_id)) < 0) + return fail_message(connection); + } + + if (info->will_topic != NULL && info->will_topic[0] != '\0') + { + if (append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) + return fail_message(connection); + + if (append_string(connection, info->will_message, strlen(info->will_message)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_WILL; + if (info->will_retain) + variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; + variable_header->flags |= (info->will_qos & 3) << 3; + } + + if (info->username != NULL && info->username[0] != '\0') + { + if (append_string(connection, info->username, strlen(info->username)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; + } + + if (info->password != NULL && info->password[0] != '\0') + { + if (append_string(connection, info->password, strlen(info->password)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; + } + + return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); +} + +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) +{ + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if (qos > 0) + { + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + } + else + *message_id = 0; + + if (connection->message.length + data_length > connection->buffer_length) + return fail_message(connection); + memcpy(connection->buffer + connection->message.length, data, data_length); + connection->message.length += data_length; + + return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) +{ + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if (connection->message.length + 1 > connection->buffer_length) + return fail_message(connection); + connection->buffer[connection->message.length++] = qos; + + return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) +{ + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); +} diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.h b/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.h new file mode 100644 index 000000000..be3cc55cb --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/mqtt_msg.h @@ -0,0 +1,141 @@ +/* + * File: mqtt_msg.h + * Author: Minh Tuan + * + * Created on July 12, 2014, 1:05 PM + */ + +#ifndef MQTT_MSG_H +#define MQTT_MSG_H +#include "user_config.h" +#include "c_types.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* +* Copyright (c) 2014, Stephen Robinson +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +/* 7 6 5 4 3 2 1 0*/ +/*| --- Message Type---- | DUP Flag | QoS Level | Retain | +/* Remaining Length */ + + +enum mqtt_message_type +{ + MQTT_MSG_TYPE_CONNECT = 1, + MQTT_MSG_TYPE_CONNACK = 2, + MQTT_MSG_TYPE_PUBLISH = 3, + MQTT_MSG_TYPE_PUBACK = 4, + MQTT_MSG_TYPE_PUBREC = 5, + MQTT_MSG_TYPE_PUBREL = 6, + MQTT_MSG_TYPE_PUBCOMP = 7, + MQTT_MSG_TYPE_SUBSCRIBE = 8, + MQTT_MSG_TYPE_SUBACK = 9, + MQTT_MSG_TYPE_UNSUBSCRIBE = 10, + MQTT_MSG_TYPE_UNSUBACK = 11, + MQTT_MSG_TYPE_PINGREQ = 12, + MQTT_MSG_TYPE_PINGRESP = 13, + MQTT_MSG_TYPE_DISCONNECT = 14 +}; + +enum mqtt_connect_return_code +{ + CONNECTION_ACCEPTED = 0, + CONNECTION_REFUSE_PROTOCOL, + CONNECTION_REFUSE_ID_REJECTED, + CONNECTION_REFUSE_SERVER_UNAVAILABLE, + CONNECTION_REFUSE_BAD_USERNAME, + CONNECTION_REFUSE_NOT_AUTHORIZED +}; + +typedef struct mqtt_message +{ + uint8_t* data; + uint16_t length; + +} mqtt_message_t; + +typedef struct mqtt_connection +{ + mqtt_message_t message; + + uint16_t message_id; + uint8_t* buffer; + uint16_t buffer_length; + +} mqtt_connection_t; + +typedef struct mqtt_connect_info +{ + char* client_id; + char* username; + char* password; + char* will_topic; + char* will_message; + uint32_t keepalive; + int will_qos; + int will_retain; + int clean_session; + +} mqtt_connect_info_t; + + +static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } +static inline int ICACHE_FLASH_ATTR mqtt_get_connect_return_code(uint8_t* buffer) { return buffer[3]; } +static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } +static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } +static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } + +void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); +int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length); +const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); +const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); +uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length); + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); +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); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection); + + +#ifdef __cplusplus +} +#endif + +#endif /* MQTT_MSG_H */ + diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/proto.c b/lib/TasmotaMqtt-1.1.0/src/mqtt/proto.c new file mode 100644 index 000000000..84078b233 --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/proto.c @@ -0,0 +1,129 @@ +#include "proto.h" +#include "ringbuf.h" +I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize) +{ + parser->buf = buf; + parser->bufSize = bufSize; + parser->dataLen = 0; + parser->callback = completeCallback; + parser->isEsc = 0; + return 0; +} + +I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value) +{ + switch (value) { + case 0x7D: + parser->isEsc = 1; + break; + + case 0x7E: + parser->dataLen = 0; + parser->isEsc = 0; + parser->isBegin = 1; + break; + + case 0x7F: + if (parser->callback != NULL) + parser->callback(); + parser->isBegin = 0; + return 0; + break; + + default: + if (parser->isBegin == 0) break; + + if (parser->isEsc) { + value ^= 0x20; + parser->isEsc = 0; + } + + if (parser->dataLen < parser->bufSize) + parser->buf[parser->dataLen++] = value; + + break; + } + return -1; +} + +I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len) +{ + while (len--) + PROTO_ParseByte(parser, *buf++); + + return 0; +} +I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, U8 *bufOut, U16* len, U16 maxBufLen) +{ + U8 c; + + PROTO_PARSER proto; + PROTO_Init(&proto, NULL, bufOut, maxBufLen); + while (RINGBUF_Get(rb, &c) == 0) { + if (PROTO_ParseByte(&proto, c) == 0) { + *len = proto.dataLen; + return 0; + } + } + return -1; +} +I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize) +{ + U16 i = 2; + U16 len = *(U16*) packet; + + if (bufSize < 1) return -1; + + *buf++ = 0x7E; + bufSize--; + + while (len--) { + switch (*packet) { + case 0x7D: + case 0x7E: + case 0x7F: + if (bufSize < 2) return -1; + *buf++ = 0x7D; + *buf++ = *packet++ ^ 0x20; + i += 2; + bufSize -= 2; + break; + default: + if (bufSize < 1) return -1; + *buf++ = *packet++; + i++; + bufSize--; + break; + } + } + + if (bufSize < 1) return -1; + *buf++ = 0x7F; + + return i; +} + +I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len) +{ + U16 i = 2; + if (RINGBUF_Put(rb, 0x7E) == -1) return -1; + while (len--) { + switch (*packet) { + case 0x7D: + case 0x7E: + case 0x7F: + if (RINGBUF_Put(rb, 0x7D) == -1) return -1; + if (RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; + i += 2; + break; + default: + if (RINGBUF_Put(rb, *packet++) == -1) return -1; + i++; + break; + } + } + if (RINGBUF_Put(rb, 0x7F) == -1) return -1; + + return i; +} + diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/proto.h b/lib/TasmotaMqtt-1.1.0/src/mqtt/proto.h new file mode 100644 index 000000000..a405bcb95 --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/proto.h @@ -0,0 +1,32 @@ +/* + * File: proto.h + * Author: ThuHien + * + * Created on November 23, 2012, 8:57 AM + */ + +#ifndef _PROTO_H_ +#define _PROTO_H_ +#include +#include "typedef.h" +#include "ringbuf.h" + +typedef void(PROTO_PARSE_CALLBACK)(); + +typedef struct { + U8 *buf; + U16 bufSize; + U16 dataLen; + U8 isEsc; + U8 isBegin; + PROTO_PARSE_CALLBACK* callback; +} PROTO_PARSER; + +I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize); +I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len); +I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize); +I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len); +I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value); +I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF *rb, U8 *bufOut, U16* len, U16 maxBufLen); +#endif + diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/queue.c b/lib/TasmotaMqtt-1.1.0/src/mqtt/queue.c new file mode 100644 index 000000000..5e4216d0f --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/queue.c @@ -0,0 +1,75 @@ +/* str_queue.c +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Redis nor the names of its contributors may be used +* to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ +#include "queue.h" + +#include "user_interface.h" +#include "osapi.h" +#include "os_type.h" +#include "mem.h" +#include "proto.h" + +uint8_t *last_rb_p_r; +uint8_t *last_rb_p_w; +uint32_t last_fill_cnt; + +void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize) +{ + queue->buf = (uint8_t*)os_zalloc(bufferSize); + RINGBUF_Init(&queue->rb, queue->buf, bufferSize); +} +int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len) +{ + uint32_t ret; + + last_rb_p_r = queue->rb.p_r; + last_rb_p_w = queue->rb.p_w; + last_fill_cnt = queue->rb.fill_cnt; + + ret = PROTO_AddRb(&queue->rb, buffer, len); + if (ret == -1) { + // rolling ring buffer back + queue->rb.p_r = last_rb_p_r; + queue->rb.p_w = last_rb_p_w; + queue->rb.fill_cnt = last_fill_cnt; + } + return ret; +} +int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) +{ + + return PROTO_ParseRb(&queue->rb, buffer, len, maxLen); +} + +bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue) +{ + if (queue->rb.fill_cnt <= 0) + return TRUE; + return FALSE; +} diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/queue.h b/lib/TasmotaMqtt-1.1.0/src/mqtt/queue.h new file mode 100644 index 000000000..79107f2d5 --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/queue.h @@ -0,0 +1,44 @@ +/* str_queue.h -- +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Redis nor the names of its contributors may be used +* to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef USER_QUEUE_H_ +#define USER_QUEUE_H_ +#include "os_type.h" +#include "ringbuf.h" +typedef struct { + uint8_t *buf; + RINGBUF rb; +} QUEUE; + +void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize); +int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len); +int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); +bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue); +#endif /* USER_QUEUE_H_ */ diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.c b/lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.c new file mode 100644 index 000000000..fc882fd5c --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.c @@ -0,0 +1,67 @@ +/** +* \file +* Ring Buffer library +*/ + +#include "ringbuf.h" + + +/** +* \brief init a RINGBUF object +* \param r pointer to a RINGBUF object +* \param buf pointer to a byte array +* \param size size of buf +* \return 0 if successfull, otherwise failed +*/ +I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size) +{ + if (r == NULL || buf == NULL || size < 2) return -1; + + r->p_o = r->p_r = r->p_w = buf; + r->fill_cnt = 0; + r->size = size; + + return 0; +} +/** +* \brief put a character into ring buffer +* \param r pointer to a ringbuf object +* \param c character to be put +* \return 0 if successfull, otherwise failed +*/ +I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c) +{ + if (r->fill_cnt >= r->size)return -1; // ring buffer is full, this should be atomic operation + + + r->fill_cnt++; // increase filled slots count, this should be atomic operation + + + *r->p_w++ = c; // put character into buffer + + if (r->p_w >= r->p_o + r->size) // rollback if write pointer go pass + r->p_w = r->p_o; // the physical boundary + + return 0; +} +/** +* \brief get a character from ring buffer +* \param r pointer to a ringbuf object +* \param c read character +* \return 0 if successfull, otherwise failed +*/ +I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c) +{ + if (r->fill_cnt <= 0)return -1; // ring buffer is empty, this should be atomic operation + + + r->fill_cnt--; // decrease filled slots count + + + *c = *r->p_r++; // get the character out + + if (r->p_r >= r->p_o + r->size) // rollback if write pointer go pass + r->p_r = r->p_o; // the physical boundary + + return 0; +} diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.h b/lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.h new file mode 100644 index 000000000..f1a4f7e8b --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/ringbuf.h @@ -0,0 +1,19 @@ +#ifndef _RING_BUF_H_ +#define _RING_BUF_H_ + +#include +#include +#include "typedef.h" + +typedef struct { + U8* p_o; /**< Original pointer */ + U8* volatile p_r; /**< Read pointer */ + U8* volatile p_w; /**< Write pointer */ + volatile I32 fill_cnt; /**< Number of filled slots */ + I32 size; /**< Buffer size */ +} RINGBUF; + +I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size); +I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c); +I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c); +#endif diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/typedef.h b/lib/TasmotaMqtt-1.1.0/src/mqtt/typedef.h new file mode 100644 index 000000000..887001ace --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/typedef.h @@ -0,0 +1,17 @@ +/** +* \file +* Standard Types definition +*/ + +#ifndef _TYPE_DEF_H_ +#define _TYPE_DEF_H_ + +typedef char I8; +typedef unsigned char U8; +typedef short I16; +typedef unsigned short U16; +typedef long I32; +typedef unsigned long U32; +typedef unsigned long long U64; + +#endif diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/user_config.h b/lib/TasmotaMqtt-1.1.0/src/mqtt/user_config.h new file mode 100644 index 000000000..125353ed0 --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/user_config.h @@ -0,0 +1,15 @@ +#ifndef __MQTT_CONFIG_H__ +#define __MQTT_CONFIG_H__ + +//#define MQTT_SSL_ENABLE + +#define MQTT_RECONNECT_TIMEOUT 5 /*second*/ + +//#define MQTT_BUF_SIZE 1024 +#define MQTT_BUF_SIZE 512 +#define QUEUE_BUFFER_SIZE 2048 + +//#define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/ +#define PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/ + +#endif // __MQTT_CONFIG_H__ \ No newline at end of file diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/utils.c b/lib/TasmotaMqtt-1.1.0/src/mqtt/utils.c new file mode 100644 index 000000000..ac4c9272b --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/utils.c @@ -0,0 +1,149 @@ +/* +* Copyright (c) 2014, Tuan PM +* Email: tuanpm@live.com +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#include +#include +#include +#include +#include +#include "utils.h" + + +uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str) +{ + uint8_t segs = 0; /* Segment count. */ + uint8_t chcnt = 0; /* Character count within segment. */ + uint8_t accum = 0; /* Accumulator for segment. */ + /* Catch NULL pointer. */ + if (str == 0) + return 0; + /* Process every character in string. */ + + while (*str != '\0') { + /* Segment changeover. */ + + if (*str == '.') { + /* Must have some digits in segment. */ + if (chcnt == 0) + return 0; + /* Limit number of segments. */ + if (++segs == 4) + return 0; + /* Reset segment values and restart loop. */ + chcnt = accum = 0; + str++; + continue; + } + + /* Check numeric. */ + if ((*str < '0') || (*str > '9')) + return 0; + + /* Accumulate and check segment. */ + + if ((accum = accum * 10 + *str - '0') > 255) + return 0; + /* Advance other segment specific stuff and continue loop. */ + + chcnt++; + str++; + } + + /* Check enough segments and enough characters in last segment. */ + + if (segs != 3) + return 0; + if (chcnt == 0) + return 0; + /* Address okay. */ + + return 1; +} +uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip) +{ + + /* The count of the number of bytes processed. */ + int i; + /* A pointer to the next digit to process. */ + const char * start; + + start = str; + for (i = 0; i < 4; i++) { + /* The digit being processed. */ + char c; + /* The value of this byte. */ + int n = 0; + while (1) { + c = * start; + start++; + if (c >= '0' && c <= '9') { + n *= 10; + n += c - '0'; + } + /* We insist on stopping at "." if we are still parsing + the first, second, or third numbers. If we have reached + the end of the numbers, we will allow any character. */ + else if ((i < 3 && c == '.') || i == 3) { + break; + } + else { + return 0; + } + } + if (n >= 256) { + return 0; + } + ((uint8_t*)ip)[i] = n; + } + return 1; + +} +uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s) +{ + uint32_t value = 0, digit; + int8_t c; + + while ((c = *s++)) { + if ('0' <= c && c <= '9') + digit = c - '0'; + else if ('A' <= c && c <= 'F') + digit = c - 'A' + 10; + else if ('a' <= c && c <= 'f') + digit = c - 'a' + 10; + else break; + + value = (value << 4) | digit; + } + + return value; +} + diff --git a/lib/TasmotaMqtt-1.1.0/src/mqtt/utils.h b/lib/TasmotaMqtt-1.1.0/src/mqtt/utils.h new file mode 100644 index 000000000..fe2874803 --- /dev/null +++ b/lib/TasmotaMqtt-1.1.0/src/mqtt/utils.h @@ -0,0 +1,9 @@ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include "c_types.h" + +uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s); +uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip); +uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str); +#endif diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/.gitignore b/lib/esp-mqtt-arduino-1.0.1.02.1/.gitignore new file mode 100644 index 000000000..2ee75414c --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/.gitignore @@ -0,0 +1,28 @@ +# C++ objects and libs + +*.slo +*.lo +*.o +#*.a +*.la +*.lai +*.so +*.dll +*.dylib + +#Makefile +*-build-* +build-* +*.autosave + +# .log files (usually created by QtTest - thanks to VestniK) +*.log + + +# Editors temporary files +*~ + + +#OSX +.DS_Store +._* diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/README.md b/lib/esp-mqtt-arduino-1.0.1.02.1/README.md new file mode 100644 index 000000000..a7ddbecac --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/README.md @@ -0,0 +1,15 @@ +MQTT +==== + +A Wrapper around mqtt for Arduino to be used with esp8266 modules. + +It wraps a slightly modified version of mqtt for esp8266 ported by Tuan PM. +Original code for esp: https://github.com/tuanpmt/esp_mqtt +Original code for contiki: https://github.com/esar/contiki-mqtt + + +==== + +**secure libssl:** + +If you want to use secure communication, please use the `secure`-branch! diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/examples/mqtt_pub/mqtt_pub.ino b/lib/esp-mqtt-arduino-1.0.1.02.1/examples/mqtt_pub/mqtt_pub.ino new file mode 100644 index 000000000..17b3be8db --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/examples/mqtt_pub/mqtt_pub.ino @@ -0,0 +1,102 @@ +#include +#include + +void myDataCb(String& topic, String& data); +void myPublishedCb(); +void myDisconnectedCb(); +void myConnectedCb(); + +#define CLIENT_ID "client1" + +// create MQTT object +MQTT myMqtt(CLIENT_ID, "192.168.0.1", 1883); + +// +const char* ssid = "ssid"; +const char* password = "ssid_password"; + + +// +void setup() { + Serial.begin(115200); + delay(1000); + + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + Serial.println("Connecting to MQTT server"); + + // setup callbacks + myMqtt.onConnected(myConnectedCb); + myMqtt.onDisconnected(myDisconnectedCb); + myMqtt.onPublished(myPublishedCb); + myMqtt.onData(myDataCb); + + Serial.println("connect mqtt..."); + myMqtt.connect(); + + delay(10); +} + +// +void loop() { + + int value = analogRead(A0); + + String topic("/"); + topic += CLIENT_ID; + topic += "/value"; + + String valueStr(value); + + // publish value to topic + boolean result = myMqtt.publish(topic, valueStr); + + delay(1000); +} + + +/* + * + */ +void myConnectedCb() +{ + Serial.println("connected to MQTT server"); +} + +void myDisconnectedCb() +{ + Serial.println("disconnected. try to reconnect..."); + delay(500); + myMqtt.connect(); +} + +void myPublishedCb() +{ + //Serial.println("published."); +} + +void myDataCb(String& topic, String& data) +{ + + Serial.print(topic); + Serial.print(": "); + Serial.println(data); +} + + + diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/examples/mqtt_sub/mqtt_sub.ino b/lib/esp-mqtt-arduino-1.0.1.02.1/examples/mqtt_sub/mqtt_sub.ino new file mode 100644 index 000000000..1f7591617 --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/examples/mqtt_sub/mqtt_sub.ino @@ -0,0 +1,95 @@ +#include +#include + +void myDataCb(String& topic, String& data); +void myPublishedCb(); +void myDisconnectedCb(); +void myConnectedCb(); + +#define CLIENT_ID "client3" +#define TOPIC "/client1/value" + + +// create MQTT +MQTT myMqtt(CLIENT_ID, "192.168.0.1", 1883); + + +const char* ssid = "ssid"; +const char* password = "ssid_password"; + + +// +void setup() { + Serial.begin(115200); + delay(1000); + + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + + Serial.println("Connecting to MQTT server"); + + // setup callbacks + myMqtt.onConnected(myConnectedCb); + myMqtt.onDisconnected(myDisconnectedCb); + myMqtt.onPublished(myPublishedCb); + myMqtt.onData(myDataCb); + + Serial.println("connect mqtt..."); + myMqtt.connect(); + + Serial.println("subscribe to topic..."); + myMqtt.subscribe(TOPIC); + + delay(10); +} + +// +void loop() { +} + + +/* + * + */ +void myConnectedCb() +{ + Serial.println("connected to MQTT server"); +} + +void myDisconnectedCb() +{ + Serial.println("disconnected. try to reconnect..."); + delay(500); + myMqtt.connect(); +} + +void myPublishedCb() +{ + //Serial.println("published."); +} + +void myDataCb(String& topic, String& data) +{ + + Serial.print(topic); + Serial.print(": "); + Serial.println(data); +} + + + diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/keywords.txt b/lib/esp-mqtt-arduino-1.0.1.02.1/keywords.txt new file mode 100644 index 000000000..527b5bf0c --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/keywords.txt @@ -0,0 +1,43 @@ +####################################### +# Syntax Coloring Map For Test +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +MQTT.h KEYWORD1 + +MQTT KEYWORD1 + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +setClientId KEYWORD2 +setUserPwd KEYWORD2 + +connect KEYWORD2 +disconnect KEYWORD2 +isConnected KEYWORD2 + +publish KEYWORD2 +subscribe KEYWORD2 + +getState KEYWORD2 + +#general +onConnected KEYWORD2 +onDisconnected KEYWORD2 +onPublished KEYWORD2 +onData KEYWORD2 + +####################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/library.properties b/lib/esp-mqtt-arduino-1.0.1.02.1/library.properties new file mode 100644 index 000000000..110da0ac4 --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/library.properties @@ -0,0 +1,9 @@ +name=ESP MQTT +version=1.0.1 +author=Ingo Randolf +maintainer=Ingo Randolf +sentence=A Wrapper around mqtt for Arduino to be used with esp8266 modules. +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 +category=Communication +url=https://github.com/i-n-g-o/esp-mqtt-arduino +architectures=esp8266 \ No newline at end of file diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.cpp b/lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.cpp new file mode 100644 index 000000000..b330bad4b --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.cpp @@ -0,0 +1,269 @@ +/*//------------------------------------------------------------------------------- + * MQTT.cpp + * + * Implementation file for MQTT Wrapper + * + * Wrapper for Arduino written by Ingo Randolf during + * eTextiles Summercamp 2015. + * + * This library is intended to be used with esp8266 modules. + * + * + * This class wraps a slightly modified version of mqtt + * for esp8266 written by Tuan PM. + * Original code: https://github.com/tuanpmt/esp_mqtt + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + //-------------------------------------------------------------------------------*/ +#include "MQTT.h" + +#include "user_interface.h" +#include "osapi.h" +#include "os_type.h" +#include "mqtt/debug.h" + + +//------------------------------------------------------------------------------------ +// mqtt internal callbacks +//------------------------------------------------------------------------------------ +static void mqttConnectedCb(uint32_t *args) +{ + MQTT_Client* client = (MQTT_Client*)args; + + MQTT* _this = (MQTT*)client->user_data; + + if (_this && _this->onMqttConnectedCb) { + _this->onMqttConnectedCb(); + } +} + +static void mqttDisconnectedCb(uint32_t *args) +{ + MQTT_Client* client = (MQTT_Client*)args; + + MQTT* _this = (MQTT*)client->user_data; + + if (_this && _this->onMqttDisconnectedCb) { + _this->onMqttDisconnectedCb(); + } +} + +static void mqttPublishedCb(uint32_t *args) +{ + MQTT_Client* client = (MQTT_Client*)args; + + MQTT* _this = (MQTT*)client->user_data; + + if (_this && _this->onMqttPublishedCb) { + _this->onMqttPublishedCb(); + } +} + +static void mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) +{ + MQTT_Client* client = (MQTT_Client*)args; + + MQTT* _this = (MQTT*)client->user_data; + + if (_this) { + + _this->_onMqttDataCb(topic, topic_len, data, data_len); + } +} + +static void mqttTimeoutCb(uint32_t *args) +{ + MQTT_Client* client = (MQTT_Client*)args; + + MQTT* _this = (MQTT*)client->user_data; + +// if (_this && _this->onMqttTimeoutCb) { +// _this->onMqttTimeoutCb(); +// } +} + + +//------------------------------------------------------------------------------------ +// MQTT class implementation +//------------------------------------------------------------------------------------ + +//MQTT::MQTT(const char* client_id, const char* host, uint32_t port) : +MQTT::MQTT(const char* client_id, const char* host, uint32_t port, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) : + onMqttConnectedCb(0) + ,onMqttDisconnectedCb(0) + ,onMqttPublishedCb(0) + ,onMqttDataCb(0) + ,onMqttDataRawCb(0) +{ + // init connections + MQTT_InitConnection(&mqttClient, (uint8_t*)host, port, 0); + + // init client + if ( !MQTT_InitClient(&mqttClient, (uint8_t*)client_id, (uint8_t*)"", (uint8_t*)"", 15, 1) ) { + MQTT_INFO("Failed to initialize properly. Check MQTT version.\r\n"); + } + + // init LWT +// MQTT_InitLWT(&mqttClient, (uint8_t*)"/lwt", (uint8_t*)"offline", 0, 0); + MQTT_InitLWT(&mqttClient, (uint8_t*)willTopic, (uint8_t*)willMessage, willQos, (uint8_t)willRetain); + + // set user data + mqttClient.user_data = (void*)this; + + // setup callbacks + MQTT_OnConnected(&mqttClient, mqttConnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); + MQTT_OnPublished(&mqttClient, mqttPublishedCb); + MQTT_OnData(&mqttClient, mqttDataCb); + + MQTT_OnTimeout(&mqttClient, mqttTimeoutCb); +} + + +MQTT::~MQTT() +{ + MQTT_DeleteClient(&mqttClient); +} + + +/* + */ +void MQTT::setClientId(const char* client_id) +{ + MQTT_SetUserId(&mqttClient, client_id); +} + +void MQTT::setUserPwd(const char* user, const char* pwd) +{ + MQTT_SetUserPwd(&mqttClient, user, pwd); +} + + +/* + */ +void MQTT::connect() +{ + MQTT_Connect(&mqttClient); +} + +void MQTT::disconnect() +{ + MQTT_Disconnect(&mqttClient); +} + +bool MQTT::isConnected() +{ + return (mqttClient.connState >= TCP_CONNECTED); +} + +/* + */ +bool MQTT::publish(const char* topic, const char* buf, uint32_t buf_len, int qos, int retain) +{ + return MQTT_Publish(&mqttClient, topic, buf, buf_len, qos, retain); +} + +bool MQTT::publish(String& topic, String& data, int qos, int retain) +{ + return publish(topic.c_str(), data.c_str(), data.length(), qos, retain); +} + +bool MQTT::publish(String& topic, const char* buf, uint32_t buf_len, int qos, int retain) +{ + return publish(topic.c_str(), buf, buf_len, qos, retain); +} + +bool MQTT::publish(const char* topic, String& data, int qos, int retain) +{ + return publish(topic, data.c_str(), data.length(), qos, retain); +} + + +/* + */ +bool MQTT::subscribe(const char* topic, uint8_t qos) +{ + return MQTT_Subscribe(&mqttClient, (char*)topic, qos); +} + +bool MQTT::subscribe(const String& topic, uint8_t qos) +{ + return MQTT_Subscribe(&mqttClient, (char*)topic.c_str(), qos); +} + + + +//------------------------------------------------------------------------------- +// set user callback functions +//------------------------------------------------------------------------------- +void MQTT::onConnected( void (*function)(void) ) +{ + onMqttConnectedCb = function; +} + +void MQTT::onDisconnected( void (*function)(void) ) +{ + onMqttDisconnectedCb = function; +} + +void MQTT::onPublished( void (*function)(void) ) +{ + onMqttPublishedCb = function; +} + +void MQTT::onData( void (*function)(String&, String&) ) +{ + onMqttDataCb = function; +} + +void MQTT::onData( void (*function)(const char*, uint32_t, const char*, uint32_t) ) +{ + onMqttDataRawCb = function; +} + + +// internal callback, calling user CB +void MQTT::_onMqttDataCb(const char* topic, uint32_t topic_len, const char* buf, uint32_t buf_len) +{ + if (onMqttDataRawCb) { + onMqttDataRawCb(topic, topic_len, buf, buf_len); + } + + if (onMqttDataCb) { + + char* topicCpy = (char*)malloc(topic_len+1); + memcpy(topicCpy, topic, topic_len); + topicCpy[topic_len] = 0; + // string it + String topicStr(topicCpy); + + char* bufCpy = (char*)malloc(buf_len+1); + memcpy(bufCpy, buf, buf_len); + bufCpy[buf_len] = 0; + // string it + String bufStr(bufCpy); + + onMqttDataCb(topicStr, bufStr); + + free(topicCpy); + free(bufCpy); + } +} + + + + + + + + + diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.h new file mode 100644 index 000000000..8838ef482 --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/MQTT.h @@ -0,0 +1,93 @@ +/*//------------------------------------------------------------------------------- + * MQTT.h + * + * Header file for MQTT Wrapper + * + * Wrapper for Arduino written by Ingo Randolf during + * eTextiles Summercamp 2015. + * + * This library is intended to be used with esp8266 modules. + * + * + * This class wraps a slightly modified version of mqtt + * for esp8266 written by Tuan PM. + * Original code: https://github.com/tuanpmt/esp_mqtt + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + //-------------------------------------------------------------------------------*/ +#ifndef MQTT_WRAPPER_H +#define MQTT_WRAPPER_H + +#include + +#include +#include +#include +#include +#include + +extern "C" { + #include + #include "mqtt/mqtt.h" +} + +class MQTT +{ +public: +// MQTT(const char* client_id, const char* host, uint32_t port); + MQTT(const char* client_id, const char* host, uint32_t port, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); + + ~MQTT(); + + void setClientId(const char* client_id); + void setUserPwd(const char* user, const char* pwd); + + void connect(); + void disconnect(); + bool isConnected(); + + bool publish(const char* topic, const char* buf, uint32_t buf_len, int qos = 0, int retain = 0); + bool publish(String& topic, String& data, int qos = 0, int retain = 0); + bool publish(String& topic, const char* buf, uint32_t buf_len, int qos = 0, int retain = 0); + bool publish(const char* topic, String& data, int qos = 0, int retain = 0); + + bool subscribe(const char* topic, uint8_t qos = 0); + bool subscribe(const String& topic, uint8_t qos = 0); + + int getState() { return mqttClient.connState; }; + + // set callbacks + void onConnected( void (*)(void) ); + void onDisconnected( void (*)(void) ); + void onPublished( void (*)(void) ); + void onData( void (*)(String&, String&) ); + void onData( void (*)(const char*, uint32_t, const char*, uint32_t) ); + + // user callbacks + void (*onMqttConnectedCb)(void); + void (*onMqttDisconnectedCb)(void); + void (*onMqttPublishedCb)(void); + void (*onMqttDataCb) (String&, String&); + void (*onMqttDataRawCb) (const char*, uint32_t, const char*, uint32_t); + + // internal callback + void _onMqttDataCb(const char*, uint32_t, const char*, uint32_t); + +private: + MQTT_Client mqttClient; + +}; + + + + +#endif \ No newline at end of file diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/debug.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/debug.h new file mode 100644 index 000000000..f45dd6d8d --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/debug.h @@ -0,0 +1,19 @@ +/* + * debug.h + * + * Created on: Dec 4, 2014 + * Author: Minh + */ + +#ifndef USER_DEBUG_H_ +#define USER_DEBUG_H_ + + +#if defined(MQTT_DEBUG_ON) +#define MQTT_INFO( format, ... ) os_printf( format, ## __VA_ARGS__ ) +#else +#define MQTT_INFO( format, ... ) +#endif + + +#endif /* USER_DEBUG_H_ */ diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt.c b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt.c new file mode 100644 index 000000000..ed63d713a --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt.c @@ -0,0 +1,1048 @@ +/* mqtt.c +* Protocol: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Redis nor the names of its contributors may be used +* to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "user_interface.h" +#include "osapi.h" +#include "espconn.h" +#include "os_type.h" +#include "mem.h" +#include "mqtt_msg.h" +#include "debug.h" +#include "user_config.h" +#include "mqtt.h" +#include "queue.h" + +#define MQTT_TASK_PRIO 1 +#define MQTT_TASK_QUEUE_SIZE 1 +#define MQTT_SEND_TIMOUT 5 + +#ifndef MQTT_SSL_SIZE +#define MQTT_SSL_SIZE 5120 +#endif + +#ifndef QUEUE_BUFFER_SIZE +#define QUEUE_BUFFER_SIZE 2048 +#endif + + +os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; + +#ifdef PROTOCOL_NAMEv311 +LOCAL uint8_t zero_len_id[2] = { 0, 0 }; +#endif + +LOCAL void ICACHE_FLASH_ATTR +mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + struct espconn *pConn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pConn->reverse; + + + if (ipaddr == NULL) + { + MQTT_INFO("DNS: Found, but got no ip, try to reconnect\r\n"); + client->connState = TCP_RECONNECT_REQ; + return; + } + + MQTT_INFO("DNS: found ip %d.%d.%d.%d\n", + *((uint8 *) &ipaddr->addr), + *((uint8 *) &ipaddr->addr + 1), + *((uint8 *) &ipaddr->addr + 2), + *((uint8 *) &ipaddr->addr + 3)); + + if (client->ip.addr == 0 && ipaddr->addr != 0) + { + os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE); + espconn_secure_connect(client->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_connect(client->pCon); + } + + client->connState = TCP_CONNECTING; + MQTT_INFO("TCP: connecting...\r\n"); + } + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + + + +LOCAL void ICACHE_FLASH_ATTR +deliver_publish(MQTT_Client* client, uint8_t* message, int length) +{ + mqtt_event_data_t event_data; + + event_data.topic_length = length; + event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); + event_data.data_length = length; + event_data.data = mqtt_get_publish_data(message, &event_data.data_length); + + if (client->dataCb) + client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); + +} + +void ICACHE_FLASH_ATTR +mqtt_send_keepalive(MQTT_Client *client) +{ + MQTT_INFO("\r\nMQTT: Send keepalive packet to %s:%d!\r\n", client->host, client->port); + client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); + client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ; + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); + err_t result = ESPCONN_OK; + if (client->security) { +#ifdef MQTT_SSL_ENABLE + result = espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + result = espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + } + + client->mqtt_state.outbound_message = NULL; + if (ESPCONN_OK == result) { + client->keepAliveTick = 0; + client->connState = MQTT_DATA; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } + else { + client->connState = TCP_RECONNECT_DISCONNECTING; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } +} + +/** + * @brief Delete tcp client and free all memory + * @param mqttClient: The mqtt client which contain TCP client + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_delete(MQTT_Client *mqttClient) +{ + if (mqttClient->pCon != NULL) { + MQTT_INFO("TCP: Free memory\r\n"); + // Force abort connections + espconn_abort(mqttClient->pCon); + // Delete connections + espconn_delete(mqttClient->pCon); + + if (mqttClient->pCon->proto.tcp) { + os_free(mqttClient->pCon->proto.tcp); + mqttClient->pCon->proto.tcp = NULL; + } + os_free(mqttClient->pCon); + mqttClient->pCon = NULL; + } +} + +/** + * @brief Delete MQTT client and free all memory + * @param mqttClient: The mqtt client + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_client_delete(MQTT_Client *mqttClient) +{ + if (mqttClient == NULL) + return; + + if (mqttClient->pCon != NULL) { + mqtt_tcpclient_delete(mqttClient); + } + + if (mqttClient->host != NULL) { + os_free(mqttClient->host); + mqttClient->host = NULL; + } + + if (mqttClient->user_data != NULL) { + os_free(mqttClient->user_data); + mqttClient->user_data = NULL; + } + + if (mqttClient->mqtt_state.in_buffer != NULL) { + os_free(mqttClient->mqtt_state.in_buffer); + mqttClient->mqtt_state.in_buffer = NULL; + } + + if (mqttClient->mqtt_state.out_buffer != NULL) { + os_free(mqttClient->mqtt_state.out_buffer); + mqttClient->mqtt_state.out_buffer = NULL; + } + + if (mqttClient->mqtt_state.outbound_message != NULL) { + if (mqttClient->mqtt_state.outbound_message->data != NULL) + { + os_free(mqttClient->mqtt_state.outbound_message->data); + mqttClient->mqtt_state.outbound_message->data = NULL; + } + } + + if (mqttClient->mqtt_state.mqtt_connection.buffer != NULL) { + // Already freed but not NULL + mqttClient->mqtt_state.mqtt_connection.buffer = NULL; + } + + if (mqttClient->connect_info.client_id != NULL) { +#ifdef PROTOCOL_NAMEv311 + /* Don't attempt to free if it's the zero_len array */ + if ( ((uint8_t*)mqttClient->connect_info.client_id) != zero_len_id ) + os_free(mqttClient->connect_info.client_id); +#else + os_free(mqttClient->connect_info.client_id); +#endif + mqttClient->connect_info.client_id = NULL; + } + + if (mqttClient->connect_info.username != NULL) { + os_free(mqttClient->connect_info.username); + mqttClient->connect_info.username = NULL; + } + + if (mqttClient->connect_info.password != NULL) { + os_free(mqttClient->connect_info.password); + mqttClient->connect_info.password = NULL; + } + + if (mqttClient->connect_info.will_topic != NULL) { + os_free(mqttClient->connect_info.will_topic); + mqttClient->connect_info.will_topic = NULL; + } + + if (mqttClient->connect_info.will_message != NULL) { + os_free(mqttClient->connect_info.will_message); + mqttClient->connect_info.will_message = NULL; + } + + if (mqttClient->msgQueue.buf != NULL) { + os_free(mqttClient->msgQueue.buf); + mqttClient->msgQueue.buf = NULL; + } + + // Initialize state + mqttClient->connState = WIFI_INIT; + // Clear callback functions to avoid abnormal callback + mqttClient->connectedCb = NULL; + mqttClient->disconnectedCb = NULL; + mqttClient->publishedCb = NULL; + mqttClient->timeoutCb = NULL; + mqttClient->dataCb = NULL; + + MQTT_INFO("MQTT: client already deleted\r\n"); +} + + +/** + * @brief Client received callback function. + * @param arg: contain the ip link information + * @param pdata: received data + * @param len: the lenght of received data + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len) +{ + uint8_t msg_type; + uint8_t msg_qos; + uint16_t msg_id; + uint8_t msg_conn_ret; + + struct espconn *pCon = (struct espconn*)arg; + MQTT_Client *client = (MQTT_Client *)pCon->reverse; + +READPACKET: + MQTT_INFO("TCP: data received %d bytes\r\n", len); + // MQTT_INFO("STATE: %d\r\n", client->connState); + if (len < MQTT_BUF_SIZE && len > 0) { + os_memcpy(client->mqtt_state.in_buffer, pdata, len); + + msg_type = mqtt_get_type(client->mqtt_state.in_buffer); + msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); + msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); + switch (client->connState) { + case MQTT_CONNECT_SENDING: + if (msg_type == MQTT_MSG_TYPE_CONNACK) { + if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT) { + MQTT_INFO("MQTT: Invalid packet\r\n"); + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_disconnect(client->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_disconnect(client->pCon); + } + } else { + msg_conn_ret = mqtt_get_connect_return_code(client->mqtt_state.in_buffer); + switch (msg_conn_ret) { + case CONNECTION_ACCEPTED: + MQTT_INFO("MQTT: Connected to %s:%d\r\n", client->host, client->port); + client->connState = MQTT_DATA; + if (client->connectedCb) + client->connectedCb((uint32_t*)client); + break; + case CONNECTION_REFUSE_PROTOCOL: + case CONNECTION_REFUSE_SERVER_UNAVAILABLE: + case CONNECTION_REFUSE_BAD_USERNAME: + case CONNECTION_REFUSE_NOT_AUTHORIZED: + MQTT_INFO("MQTT: Connection refuse, reason code: %d\r\n", msg_conn_ret); + default: + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_disconnect(client->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_disconnect(client->pCon); + } + + } + + } + + } + break; + case MQTT_DATA: + case MQTT_KEEPALIVE_SEND: + client->mqtt_state.message_length_read = len; + client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + + + switch (msg_type) + { + + case MQTT_MSG_TYPE_SUBACK: + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) + MQTT_INFO("MQTT: Subscribe successful\r\n"); + break; + case MQTT_MSG_TYPE_UNSUBACK: + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) + MQTT_INFO("MQTT: UnSubscribe successful\r\n"); + break; + case MQTT_MSG_TYPE_PUBLISH: + if (msg_qos == 1) + client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); + else if (msg_qos == 2) + client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); + if (msg_qos == 1 || msg_qos == 2) { + MQTT_INFO("MQTT: Queue response QoS: %d\r\n", msg_qos); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + } + } + + deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + break; + case MQTT_MSG_TYPE_PUBACK: + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { + MQTT_INFO("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\r\n"); + } + + break; + case MQTT_MSG_TYPE_PUBREC: + client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + } + break; + case MQTT_MSG_TYPE_PUBREL: + client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + } + break; + case MQTT_MSG_TYPE_PUBCOMP: + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { + MQTT_INFO("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\r\n"); + } + break; + case MQTT_MSG_TYPE_PINGREQ: + client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + } + break; + case MQTT_MSG_TYPE_PINGRESP: + // Ignore + break; + } + // NOTE: this is done down here and not in the switch case above + // because the PSOCK_READBUF_LEN() won't work inside a switch + // statement due to the way protothreads resume. + if (msg_type == MQTT_MSG_TYPE_PUBLISH) + { + len = client->mqtt_state.message_length_read; + + if (client->mqtt_state.message_length < client->mqtt_state.message_length_read) + { + //client->connState = MQTT_PUBLISH_RECV; + //Not Implement yet + len -= client->mqtt_state.message_length; + pdata += client->mqtt_state.message_length; + + MQTT_INFO("Get another published message\r\n"); + goto READPACKET; + } + + } + break; + } + } else { + MQTT_INFO("ERROR: Message too long\r\n"); + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** + * @brief Client send over callback function. + * @param arg: contain the ip link information + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_sent_cb(void *arg) +{ + struct espconn *pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + MQTT_INFO("TCP: Sent\r\n"); + client->sendTimeout = 0; + client->keepAliveTick = 0; + + if ((client->connState == MQTT_DATA || client->connState == MQTT_KEEPALIVE_SEND) + && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH) { + if (client->publishedCb) + client->publishedCb((uint32_t*)client); + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +void ICACHE_FLASH_ATTR mqtt_timer(void *arg) +{ + MQTT_Client* client = (MQTT_Client*)arg; + + if (client->connState == MQTT_DATA) { + client->keepAliveTick ++; + if (client->keepAliveTick > (client->mqtt_state.connect_info->keepalive / 2)) { + client->connState = MQTT_KEEPALIVE_SEND; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } + + } else if (client->connState == TCP_RECONNECT_REQ) { + client->reconnectTick ++; + if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { + client->reconnectTick = 0; + client->connState = TCP_RECONNECT; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + if (client->timeoutCb) + client->timeoutCb((uint32_t*)client); + } + } + if (client->sendTimeout > 0) + client->sendTimeout --; +} + +void ICACHE_FLASH_ATTR +mqtt_tcpclient_discon_cb(void *arg) +{ + + struct espconn *pespconn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pespconn->reverse; + MQTT_INFO("TCP: Disconnected callback\r\n"); + if (TCP_DISCONNECTING == client->connState) { + client->connState = TCP_DISCONNECTED; + } + else if (MQTT_DELETING == client->connState) { + client->connState = MQTT_DELETED; + } + else { + client->connState = TCP_RECONNECT_REQ; + } + if (client->disconnectedCb) + client->disconnectedCb((uint32_t*)client); + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + + + +/** + * @brief Tcp client connect success callback function. + * @param arg: contain the ip link information + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_connect_cb(void *arg) +{ + struct espconn *pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + + espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); + espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);//////// + espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);/////// + MQTT_INFO("MQTT: Connected to broker %s:%d\r\n", client->host, client->port); + + mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); + client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + } + + client->mqtt_state.outbound_message = NULL; + client->connState = MQTT_CONNECT_SENDING; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** + * @brief Tcp client connect repeat callback function. + * @param arg: contain the ip link information + * @retval None + */ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_recon_cb(void *arg, sint8 errType) +{ + struct espconn *pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + + MQTT_INFO("TCP: Reconnect to %s:%d\r\n", client->host, client->port); + + client->connState = TCP_RECONNECT_REQ; + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + +} + +/** + * @brief MQTT publish function. + * @param client: MQTT_Client reference + * @param topic: string topic will publish to + * @param data: buffer data send point to + * @param data_length: length of data + * @param qos: qos + * @param retain: retain + * @retval TRUE if success queue + */ +BOOL ICACHE_FLASH_ATTR +MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain) +{ + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, + topic, data, data_length, + qos, retain, + &client->mqtt_state.pending_msg_id); + if (client->mqtt_state.outbound_message->length == 0) { + MQTT_INFO("MQTT: Queuing publish failed\r\n"); + return FALSE; + } + 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); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + MQTT_INFO("MQTT: Serious buffer error\r\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +/** + * @brief MQTT subscibe function. + * @param client: MQTT_Client reference + * @param topic: string topic will subscribe + * @param qos: qos + * @retval TRUE if success queue + */ +BOOL ICACHE_FLASH_ATTR +MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos) +{ + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + + client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, + topic, qos, + &client->mqtt_state.pending_msg_id); + MQTT_INFO("MQTT: queue subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + MQTT_INFO("MQTT: Serious buffer error\r\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + + return TRUE; +} + +/** + * @brief MQTT un-subscibe function. + * @param client: MQTT_Client reference + * @param topic: String topic will un-subscribe + * @retval TRUE if success queue + */ +BOOL ICACHE_FLASH_ATTR +MQTT_UnSubscribe(MQTT_Client *client, char* topic) +{ + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection, + topic, + &client->mqtt_state.pending_msg_id); + MQTT_INFO("MQTT: queue un-subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + MQTT_INFO("MQTT: Serious buffer error\r\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +/** + * @brief MQTT ping function. + * @param client: MQTT_Client reference + * @retval TRUE if success queue + */ +BOOL ICACHE_FLASH_ATTR +MQTT_Ping(MQTT_Client *client) +{ + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); + if (client->mqtt_state.outbound_message->length == 0) { + MQTT_INFO("MQTT: Queuing publish failed\r\n"); + return FALSE; + } + 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); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + MQTT_INFO("MQTT: Queue full\r\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + MQTT_INFO("MQTT: Serious buffer error\r\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +void ICACHE_FLASH_ATTR +MQTT_Task(os_event_t *e) +{ + MQTT_Client* client = (MQTT_Client*)e->par; + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + if (e->par == 0) + return; + switch (client->connState) { + + case TCP_RECONNECT_REQ: + break; + case TCP_RECONNECT: + mqtt_tcpclient_delete(client); + MQTT_Connect(client); + MQTT_INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port); + client->connState = TCP_CONNECTING; + break; + case MQTT_DELETING: + case TCP_DISCONNECTING: + case TCP_RECONNECT_DISCONNECTING: + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_disconnect(client->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_disconnect(client->pCon); + } + break; + case TCP_DISCONNECTED: + MQTT_INFO("MQTT: Disconnected\r\n"); + mqtt_tcpclient_delete(client); + break; + case MQTT_DELETED: + MQTT_INFO("MQTT: Deleted client\r\n"); + mqtt_client_delete(client); + break; + case MQTT_KEEPALIVE_SEND: + mqtt_send_keepalive(client); + break; + case MQTT_DATA: + if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) { + break; + } + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0) { + client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); + client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); + client->keepAliveTick = 0; + if (client->security) { +#ifdef MQTT_SSL_ENABLE + espconn_secure_send(client->pCon, dataBuffer, dataLen); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else { + espconn_send(client->pCon, dataBuffer, dataLen); + } + + client->mqtt_state.outbound_message = NULL; + break; + } + break; + } +} + +/** + * @brief MQTT initialization connection function + * @param client: MQTT_Client reference + * @param host: Domain or IP string + * @param port: Port to connect + * @param security: 1 for ssl, 0 for none + * @retval None + */ +void ICACHE_FLASH_ATTR +MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security) +{ + uint32_t temp; + MQTT_INFO("MQTT:InitConnection\r\n"); + os_memset(mqttClient, 0, sizeof(MQTT_Client)); + temp = os_strlen(host); + mqttClient->host = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->host, host); + mqttClient->host[temp] = 0; + mqttClient->port = port; + mqttClient->security = security; + +} + +/** + * @brief MQTT initialization mqtt client function + * @param client: MQTT_Client reference + * @param clientid: MQTT client id + * @param client_user:MQTT client user + * @param client_pass:MQTT client password + * @param client_pass:MQTT keep alive timer, in second + * @retval None + */ +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) +{ + uint32_t temp; + MQTT_INFO("MQTT:InitClient\r\n"); + + os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); + + if ( !client_id ) + { + /* Should be allowed by broker, but clean session flag must be set. */ + #ifdef PROTOCOL_NAMEv311 + if (cleanSession) + { + mqttClient->connect_info.client_id = zero_len_id; + } else { + MQTT_INFO("cleanSession must be set to use 0 length client_id\r\n"); + return false; + } + /* Not supported. Return. */ + #else + MQTT_INFO("Client ID required for MQTT < 3.1.1!\r\n"); + return false; + #endif + } + + /* If connect_info's client_id is still NULL and we get here, we can * + * assume the passed client_id is non-NULL. */ + if ( !(mqttClient->connect_info.client_id) ) + { + temp = os_strlen(client_id); + mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.client_id, client_id); + mqttClient->connect_info.client_id[temp] = 0; + } + + if (client_user) + { + temp = os_strlen(client_user); + mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.username, client_user); + mqttClient->connect_info.username[temp] = 0; + } + + if (client_pass) + { + temp = os_strlen(client_pass); + mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.password, client_pass); + mqttClient->connect_info.password[temp] = 0; + } + + + mqttClient->connect_info.keepalive = keepAliveTime; + mqttClient->connect_info.clean_session = cleanSession; + + mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); + mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; + mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); + mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; + mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; + + mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length); + + QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); + + system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); + return true; +} +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) +{ + uint32_t temp; + temp = os_strlen(will_topic); + mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.will_topic, will_topic); + mqttClient->connect_info.will_topic[temp] = 0; + + temp = os_strlen(will_msg); + mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.will_message, will_msg); + mqttClient->connect_info.will_message[temp] = 0; + + + mqttClient->connect_info.will_qos = will_qos; + mqttClient->connect_info.will_retain = will_retain; +} +/** + * @brief Begin connect to MQTT broker + * @param client: MQTT_Client reference + * @retval None + */ +void ICACHE_FLASH_ATTR +MQTT_Connect(MQTT_Client *mqttClient) +{ + if (mqttClient->pCon) { + // Clean up the old connection forcefully - using MQTT_Disconnect + // does not actually release the old connection until the + // disconnection callback is invoked. + mqtt_tcpclient_delete(mqttClient); + } + mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); + mqttClient->pCon->type = ESPCONN_TCP; + mqttClient->pCon->state = ESPCONN_NONE; + mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); + mqttClient->pCon->proto.tcp->local_port = espconn_port(); + mqttClient->pCon->proto.tcp->remote_port = mqttClient->port; + mqttClient->pCon->reverse = mqttClient; + espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); + espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); + + mqttClient->keepAliveTick = 0; + mqttClient->reconnectTick = 0; + + + os_timer_disarm(&mqttClient->mqttTimer); + os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); + os_timer_arm(&mqttClient->mqttTimer, 1000, 1); + + if (UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip)) { + MQTT_INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port); + if (mqttClient->security) + { +#ifdef MQTT_SSL_ENABLE + espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE); + espconn_secure_connect(mqttClient->pCon); +#else + MQTT_INFO("TCP: Do not support SSL\r\n"); +#endif + } + else + { + espconn_connect(mqttClient->pCon); + } + } + else { + MQTT_INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port); + espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found); + } + mqttClient->connState = TCP_CONNECTING; +} + +void ICACHE_FLASH_ATTR +MQTT_Disconnect(MQTT_Client *mqttClient) +{ + mqttClient->connState = TCP_DISCONNECTING; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); + os_timer_disarm(&mqttClient->mqttTimer); +} + +void ICACHE_FLASH_ATTR +MQTT_DeleteClient(MQTT_Client *mqttClient) +{ + if (NULL == mqttClient) + return; + + mqttClient->connState = MQTT_DELETED; + // if(TCP_DISCONNECTED == mqttClient->connState) { + // mqttClient->connState = MQTT_DELETED; + // } else if(MQTT_DELETED != mqttClient->connState) { + // mqttClient->connState = MQTT_DELETING; + // } + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); + os_timer_disarm(&mqttClient->mqttTimer); +} + +void ICACHE_FLASH_ATTR +MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb) +{ + mqttClient->connectedCb = connectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb) +{ + mqttClient->disconnectedCb = disconnectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb) +{ + mqttClient->dataCb = dataCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb) +{ + mqttClient->publishedCb = publishedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb) +{ + mqttClient->timeoutCb = timeoutCb; +} + + + +void ICACHE_FLASH_ATTR +MQTT_SetUserId(MQTT_Client *mqttClient, const char* client_id) +{ + if (mqttClient->connect_info.client_id != 0) { + os_free(mqttClient->connect_info.client_id); + mqttClient->connect_info.client_id = 0; + } + + + uint32_t len = os_strlen(client_id); + + mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(len + 1); + if (len) { + os_strcpy(mqttClient->connect_info.client_id, client_id); + } + mqttClient->connect_info.client_id[len] = 0; +} + +void ICACHE_FLASH_ATTR +MQTT_SetUserPwd(MQTT_Client *mqttClient, const char* user, const char* pwd) +{ + uint32_t len; + + // free username + if (mqttClient->connect_info.username != 0) { + os_free(mqttClient->connect_info.username); + mqttClient->connect_info.username = 0; + } + // free password + if (mqttClient->connect_info.password != 0) { + os_free(mqttClient->connect_info.password); + mqttClient->connect_info.password = 0; + } + + + // copy username + len = os_strlen(user); + mqttClient->connect_info.username = (uint8_t*)os_zalloc(len + 1); + if (len) { + os_strcpy(mqttClient->connect_info.username, user); + } + mqttClient->connect_info.username[len] = 0; + + + // copy password + len = os_strlen(pwd); + mqttClient->connect_info.password = (uint8_t*)os_zalloc(len + 1); + if (len) { + os_strcpy(mqttClient->connect_info.password, pwd); + } + mqttClient->connect_info.password[len] = 0; + +} + diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt.h new file mode 100644 index 000000000..6ef619433 --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt.h @@ -0,0 +1,152 @@ +/* mqtt.h +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Redis nor the names of its contributors may be used +* to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef USER_AT_MQTT_H_ +#define USER_AT_MQTT_H_ +#include "mqtt_config.h" +#include "mqtt_msg.h" +#include "user_interface.h" + +#include "queue.h" +typedef struct mqtt_event_data_t +{ + uint8_t type; + const char* topic; + const char* data; + uint16_t topic_length; + uint16_t data_length; + uint16_t data_offset; +} mqtt_event_data_t; + +typedef struct mqtt_state_t +{ + uint16_t port; + int auto_reconnect; + mqtt_connect_info_t* connect_info; + uint8_t* in_buffer; + uint8_t* out_buffer; + int in_buffer_length; + int out_buffer_length; + uint16_t message_length; + uint16_t message_length_read; + mqtt_message_t* outbound_message; + mqtt_connection_t mqtt_connection; + uint16_t pending_msg_id; + int pending_msg_type; + int pending_publish_qos; +} mqtt_state_t; + +typedef enum { + WIFI_INIT, + WIFI_CONNECTING, + WIFI_CONNECTING_ERROR, + WIFI_CONNECTED, + DNS_RESOLVE, + TCP_DISCONNECTING, + TCP_DISCONNECTED, + TCP_RECONNECT_DISCONNECTING, + TCP_RECONNECT_REQ, + TCP_RECONNECT, + TCP_CONNECTING, + TCP_CONNECTING_ERROR, + TCP_CONNECTED, + MQTT_CONNECT_SEND, + MQTT_CONNECT_SENDING, + MQTT_SUBSCIBE_SEND, + MQTT_SUBSCIBE_SENDING, + MQTT_DATA, + MQTT_KEEPALIVE_SEND, + MQTT_PUBLISH_RECV, + MQTT_PUBLISHING, + MQTT_DELETING, + MQTT_DELETED, +} tConnState; + +typedef void (*MqttCallback)(uint32_t *args); +typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh); + +typedef struct { + struct espconn *pCon; + uint8_t security; + uint8_t* host; + uint32_t port; + ip_addr_t ip; + mqtt_state_t mqtt_state; + mqtt_connect_info_t connect_info; + MqttCallback connectedCb; + MqttCallback disconnectedCb; + MqttCallback publishedCb; + MqttCallback timeoutCb; + MqttDataCallback dataCb; + ETSTimer mqttTimer; + uint32_t keepAliveTick; + uint32_t reconnectTick; + uint32_t sendTimeout; + tConnState connState; + QUEUE msgQueue; + void* user_data; +} MQTT_Client; + +#define SEC_NONSSL 0 +#define SEC_SSL 1 + +#define MQTT_FLAG_CONNECTED 1 +#define MQTT_FLAG_READY 2 +#define MQTT_FLAG_EXIT 4 + +#define MQTT_EVENT_TYPE_NONE 0 +#define MQTT_EVENT_TYPE_CONNECTED 1 +#define MQTT_EVENT_TYPE_DISCONNECTED 2 +#define MQTT_EVENT_TYPE_SUBSCRIBED 3 +#define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 +#define MQTT_EVENT_TYPE_PUBLISH 5 +#define MQTT_EVENT_TYPE_PUBLISHED 6 +#define MQTT_EVENT_TYPE_EXITED 7 +#define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 + +void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security); +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); +void ICACHE_FLASH_ATTR MQTT_DeleteClient(MQTT_Client *mqttClient); +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); + +void ICACHE_FLASH_ATTR MQTT_SetUserId(MQTT_Client *mqttClient, const char* client_id); +void ICACHE_FLASH_ATTR MQTT_SetUserPwd(MQTT_Client *mqttClient, const char* user_id, const char* pwd); + +void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb); +void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb); +void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb); +void ICACHE_FLASH_ATTR MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb); +void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb); +bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos); +bool ICACHE_FLASH_ATTR MQTT_UnSubscribe(MQTT_Client *client, char* topic); +void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient); +void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient); +bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain); + +#endif /* USER_AT_MQTT_H_ */ diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_config.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_config.h new file mode 100644 index 000000000..5332f82ee --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_config.h @@ -0,0 +1,19 @@ +#ifndef __MQTT_CONFIG_H__ +#define __MQTT_CONFIG_H__ + +//#define MQTT_SSL_ENABLE + +/*DEFAULT CONFIGURATIONS*/ + +#define MQTT_BUF_SIZE 1024 +//#define MQTT_KEEPALIVE 120 /*second*/ +#define MQTT_KEEPALIVE 15 /*second*/ + +#define MQTT_RECONNECT_TIMEOUT 5 /*second*/ + +#define QUEUE_BUFFER_SIZE 2048 + +//#define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/ +#define PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/ + +#endif // __MQTT_CONFIG_H__ \ No newline at end of file diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_msg.c b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_msg.c new file mode 100644 index 000000000..c2b320311 --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_msg.c @@ -0,0 +1,487 @@ +/* +* Copyright (c) 2014, Stephen Robinson +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include +#include "mqtt_msg.h" +#include "mqtt_config.h" +#define MQTT_MAX_FIXED_HEADER_SIZE 3 + +enum mqtt_connect_flag +{ + MQTT_CONNECT_FLAG_USERNAME = 1 << 7, + MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, + MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, + MQTT_CONNECT_FLAG_WILL = 1 << 2, + MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 +}; + +struct __attribute((__packed__)) mqtt_connect_variable_header +{ + uint8_t lengthMsb; + uint8_t lengthLsb; +#if defined(PROTOCOL_NAMEv31) + uint8_t magic[6]; +#elif defined(PROTOCOL_NAMEv311) + uint8_t magic[4]; +#else +#error "Please define protocol name" +#endif + uint8_t version; + uint8_t flags; + uint8_t keepaliveMsb; + uint8_t keepaliveLsb; +}; + +static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len) +{ + if (connection->message.length + len + 2 > connection->buffer_length) + return -1; + + connection->buffer[connection->message.length++] = len >> 8; + connection->buffer[connection->message.length++] = len & 0xff; + memcpy(connection->buffer + connection->message.length, string, len); + connection->message.length += len; + + return len + 2; +} + +static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id) +{ + // If message_id is zero then we should assign one, otherwise + // we'll use the one supplied by the caller + while (message_id == 0) + message_id = ++connection->message_id; + + if (connection->message.length + 2 > connection->buffer_length) + return 0; + + connection->buffer[connection->message.length++] = message_id >> 8; + connection->buffer[connection->message.length++] = message_id & 0xff; + + return message_id; +} + +static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection) +{ + connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; + return MQTT_MAX_FIXED_HEADER_SIZE; +} + +static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection) +{ + connection->message.data = connection->buffer; + connection->message.length = 0; + return &connection->message; +} + +static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) +{ + int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; + + if (remaining_length > 127) + { + connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[1] = 0x80 | (remaining_length % 128); + connection->buffer[2] = remaining_length / 128; + connection->message.length = remaining_length + 3; + connection->message.data = connection->buffer; + } + else + { + connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[2] = remaining_length; + connection->message.length = remaining_length + 2; + connection->message.data = connection->buffer + 1; + } + + return &connection->message; +} + +void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) +{ + memset(connection, 0, sizeof(mqtt_connection_t)); + connection->buffer = buffer; + connection->buffer_length = buffer_length; +} + +int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length) +{ + int i; + int totlen = 0; + + for (i = 1; i < length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + return totlen; +} + +const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) +{ + int i; + int totlen = 0; + int topiclen; + + for (i = 1; i < *length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + if (i + 2 >= *length) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen > *length) + return NULL; + + *length = topiclen; + return (const char*)(buffer + i); +} + +const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) +{ + int i; + int totlen = 0; + int topiclen; + int blength = *length; + *length = 0; + + for (i = 1; i < blength; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + if (i + 2 >= blength) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen >= blength) + return NULL; + + i += topiclen; + + if (mqtt_get_qos(buffer) > 0) + { + if (i + 2 >= blength) + return NULL; + i += 2; + } + + if (totlen < i) + return NULL; + + if (totlen <= blength) + *length = totlen - i; + else + *length = blength - i; + return (const char*)(buffer + i); +} + +uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length) +{ + if (length < 1) + return 0; + + switch (mqtt_get_type(buffer)) + { + case MQTT_MSG_TYPE_PUBLISH: + { + int i; + int topiclen; + + for (i = 1; i < length; ++i) + { + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + + if (i + 2 >= length) + return 0; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen >= length) + return 0; + i += topiclen; + + if (mqtt_get_qos(buffer) > 0) + { + if (i + 2 >= length) + return 0; + //i += 2; + } else { + return 0; + } + + return (buffer[i] << 8) | buffer[i + 1]; + } + case MQTT_MSG_TYPE_PUBACK: + case MQTT_MSG_TYPE_PUBREC: + case MQTT_MSG_TYPE_PUBREL: + case MQTT_MSG_TYPE_PUBCOMP: + case MQTT_MSG_TYPE_SUBACK: + case MQTT_MSG_TYPE_UNSUBACK: + case MQTT_MSG_TYPE_SUBSCRIBE: + { + // This requires the remaining length to be encoded in 1 byte, + // which it should be. + if (length >= 4 && (buffer[1] & 0x80) == 0) + return (buffer[2] << 8) | buffer[3]; + else + return 0; + } + + default: + return 0; + } +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) +{ + struct mqtt_connect_variable_header* variable_header; + + init_message(connection); + + if (connection->message.length + sizeof(*variable_header) > connection->buffer_length) + return fail_message(connection); + variable_header = (void*)(connection->buffer + connection->message.length); + connection->message.length += sizeof(*variable_header); + + variable_header->lengthMsb = 0; +#if defined(PROTOCOL_NAMEv31) + variable_header->lengthLsb = 6; + memcpy(variable_header->magic, "MQIsdp", 6); + variable_header->version = 3; +#elif defined(PROTOCOL_NAMEv311) + variable_header->lengthLsb = 4; + memcpy(variable_header->magic, "MQTT", 4); + variable_header->version = 4; +#else +#error "Please define protocol name" +#endif + + variable_header->flags = 0; + variable_header->keepaliveMsb = info->keepalive >> 8; + variable_header->keepaliveLsb = info->keepalive & 0xff; + + if (info->clean_session) + variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; + + if (info->client_id == NULL) + { + /* Never allowed */ + return fail_message(connection); + } + else if (info->client_id[0] == '\0') + { +#ifdef PROTOCOL_NAMEv311 + /* Allowed. Format 0 Length ID */ + append_string(connection, info->client_id, 2) ; +#else + /* 0 Length not allowed */ + return fail_message(connection); +#endif + } + else + { + /* No 0 data and at least 1 long. Good to go. */ + if(append_string(connection, info->client_id, strlen(info->client_id)) < 0) + return fail_message(connection); + } + + if (info->will_topic != NULL && info->will_topic[0] != '\0') + { + if (append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) + return fail_message(connection); + + if (append_string(connection, info->will_message, strlen(info->will_message)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_WILL; + if (info->will_retain) + variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; + variable_header->flags |= (info->will_qos & 3) << 3; + } + + if (info->username != NULL && info->username[0] != '\0') + { + if (append_string(connection, info->username, strlen(info->username)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; + } + + if (info->password != NULL && info->password[0] != '\0') + { + if (append_string(connection, info->password, strlen(info->password)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; + } + + return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); +} + +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) +{ + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if (qos > 0) + { + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + } + else + *message_id = 0; + + if (connection->message.length + data_length > connection->buffer_length) + return fail_message(connection); + memcpy(connection->buffer + connection->message.length, data, data_length); + connection->message.length += data_length; + + return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) +{ + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if (connection->message.length + 1 > connection->buffer_length) + return fail_message(connection); + connection->buffer[connection->message.length++] = qos; + + return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) +{ + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); +} diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_msg.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_msg.h new file mode 100644 index 000000000..be3cc55cb --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/mqtt_msg.h @@ -0,0 +1,141 @@ +/* + * File: mqtt_msg.h + * Author: Minh Tuan + * + * Created on July 12, 2014, 1:05 PM + */ + +#ifndef MQTT_MSG_H +#define MQTT_MSG_H +#include "user_config.h" +#include "c_types.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* +* Copyright (c) 2014, Stephen Robinson +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +/* 7 6 5 4 3 2 1 0*/ +/*| --- Message Type---- | DUP Flag | QoS Level | Retain | +/* Remaining Length */ + + +enum mqtt_message_type +{ + MQTT_MSG_TYPE_CONNECT = 1, + MQTT_MSG_TYPE_CONNACK = 2, + MQTT_MSG_TYPE_PUBLISH = 3, + MQTT_MSG_TYPE_PUBACK = 4, + MQTT_MSG_TYPE_PUBREC = 5, + MQTT_MSG_TYPE_PUBREL = 6, + MQTT_MSG_TYPE_PUBCOMP = 7, + MQTT_MSG_TYPE_SUBSCRIBE = 8, + MQTT_MSG_TYPE_SUBACK = 9, + MQTT_MSG_TYPE_UNSUBSCRIBE = 10, + MQTT_MSG_TYPE_UNSUBACK = 11, + MQTT_MSG_TYPE_PINGREQ = 12, + MQTT_MSG_TYPE_PINGRESP = 13, + MQTT_MSG_TYPE_DISCONNECT = 14 +}; + +enum mqtt_connect_return_code +{ + CONNECTION_ACCEPTED = 0, + CONNECTION_REFUSE_PROTOCOL, + CONNECTION_REFUSE_ID_REJECTED, + CONNECTION_REFUSE_SERVER_UNAVAILABLE, + CONNECTION_REFUSE_BAD_USERNAME, + CONNECTION_REFUSE_NOT_AUTHORIZED +}; + +typedef struct mqtt_message +{ + uint8_t* data; + uint16_t length; + +} mqtt_message_t; + +typedef struct mqtt_connection +{ + mqtt_message_t message; + + uint16_t message_id; + uint8_t* buffer; + uint16_t buffer_length; + +} mqtt_connection_t; + +typedef struct mqtt_connect_info +{ + char* client_id; + char* username; + char* password; + char* will_topic; + char* will_message; + uint32_t keepalive; + int will_qos; + int will_retain; + int clean_session; + +} mqtt_connect_info_t; + + +static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } +static inline int ICACHE_FLASH_ATTR mqtt_get_connect_return_code(uint8_t* buffer) { return buffer[3]; } +static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } +static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } +static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } + +void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); +int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length); +const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); +const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); +uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length); + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); +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); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection); + + +#ifdef __cplusplus +} +#endif + +#endif /* MQTT_MSG_H */ + diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/proto.c b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/proto.c new file mode 100644 index 000000000..84078b233 --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/proto.c @@ -0,0 +1,129 @@ +#include "proto.h" +#include "ringbuf.h" +I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize) +{ + parser->buf = buf; + parser->bufSize = bufSize; + parser->dataLen = 0; + parser->callback = completeCallback; + parser->isEsc = 0; + return 0; +} + +I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value) +{ + switch (value) { + case 0x7D: + parser->isEsc = 1; + break; + + case 0x7E: + parser->dataLen = 0; + parser->isEsc = 0; + parser->isBegin = 1; + break; + + case 0x7F: + if (parser->callback != NULL) + parser->callback(); + parser->isBegin = 0; + return 0; + break; + + default: + if (parser->isBegin == 0) break; + + if (parser->isEsc) { + value ^= 0x20; + parser->isEsc = 0; + } + + if (parser->dataLen < parser->bufSize) + parser->buf[parser->dataLen++] = value; + + break; + } + return -1; +} + +I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len) +{ + while (len--) + PROTO_ParseByte(parser, *buf++); + + return 0; +} +I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, U8 *bufOut, U16* len, U16 maxBufLen) +{ + U8 c; + + PROTO_PARSER proto; + PROTO_Init(&proto, NULL, bufOut, maxBufLen); + while (RINGBUF_Get(rb, &c) == 0) { + if (PROTO_ParseByte(&proto, c) == 0) { + *len = proto.dataLen; + return 0; + } + } + return -1; +} +I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize) +{ + U16 i = 2; + U16 len = *(U16*) packet; + + if (bufSize < 1) return -1; + + *buf++ = 0x7E; + bufSize--; + + while (len--) { + switch (*packet) { + case 0x7D: + case 0x7E: + case 0x7F: + if (bufSize < 2) return -1; + *buf++ = 0x7D; + *buf++ = *packet++ ^ 0x20; + i += 2; + bufSize -= 2; + break; + default: + if (bufSize < 1) return -1; + *buf++ = *packet++; + i++; + bufSize--; + break; + } + } + + if (bufSize < 1) return -1; + *buf++ = 0x7F; + + return i; +} + +I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len) +{ + U16 i = 2; + if (RINGBUF_Put(rb, 0x7E) == -1) return -1; + while (len--) { + switch (*packet) { + case 0x7D: + case 0x7E: + case 0x7F: + if (RINGBUF_Put(rb, 0x7D) == -1) return -1; + if (RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; + i += 2; + break; + default: + if (RINGBUF_Put(rb, *packet++) == -1) return -1; + i++; + break; + } + } + if (RINGBUF_Put(rb, 0x7F) == -1) return -1; + + return i; +} + diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/proto.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/proto.h new file mode 100644 index 000000000..a405bcb95 --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/proto.h @@ -0,0 +1,32 @@ +/* + * File: proto.h + * Author: ThuHien + * + * Created on November 23, 2012, 8:57 AM + */ + +#ifndef _PROTO_H_ +#define _PROTO_H_ +#include +#include "typedef.h" +#include "ringbuf.h" + +typedef void(PROTO_PARSE_CALLBACK)(); + +typedef struct { + U8 *buf; + U16 bufSize; + U16 dataLen; + U8 isEsc; + U8 isBegin; + PROTO_PARSE_CALLBACK* callback; +} PROTO_PARSER; + +I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize); +I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len); +I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize); +I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len); +I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value); +I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF *rb, U8 *bufOut, U16* len, U16 maxBufLen); +#endif + diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/queue.c b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/queue.c new file mode 100644 index 000000000..95bbec0cd --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/queue.c @@ -0,0 +1,75 @@ +/* str_queue.c +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Redis nor the names of its contributors may be used +* to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ +#include "queue.h" + +#include "user_interface.h" +#include "osapi.h" +#include "os_type.h" +#include "mem.h" +#include "proto.h" + +uint8_t *last_rb_p_r; +uint8_t *last_rb_p_w; +uint32_t last_fill_cnt; + +void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize) +{ + queue->buf = (uint8_t*)os_zalloc(bufferSize); + RINGBUF_Init(&queue->rb, queue->buf, bufferSize); +} +int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len) +{ + uint32_t ret; + + last_rb_p_r = queue->rb.p_r; + last_rb_p_w = queue->rb.p_w; + last_fill_cnt = queue->rb.fill_cnt; + + ret = PROTO_AddRb(&queue->rb, buffer, len); + if (ret == -1) { + // rolling ring buffer back + queue->rb.p_r = last_rb_p_r; + queue->rb.p_w = last_rb_p_w; + queue->rb.fill_cnt = last_fill_cnt; + } + return ret; +} +int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) +{ + + return PROTO_ParseRb(&queue->rb, buffer, len, maxLen); +} + +BOOL ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue) +{ + if (queue->rb.fill_cnt <= 0) + return TRUE; + return FALSE; +} diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/queue.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/queue.h new file mode 100644 index 000000000..79107f2d5 --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/queue.h @@ -0,0 +1,44 @@ +/* str_queue.h -- +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Redis nor the names of its contributors may be used +* to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef USER_QUEUE_H_ +#define USER_QUEUE_H_ +#include "os_type.h" +#include "ringbuf.h" +typedef struct { + uint8_t *buf; + RINGBUF rb; +} QUEUE; + +void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize); +int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len); +int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); +bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue); +#endif /* USER_QUEUE_H_ */ diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/ringbuf.c b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/ringbuf.c new file mode 100644 index 000000000..fc882fd5c --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/ringbuf.c @@ -0,0 +1,67 @@ +/** +* \file +* Ring Buffer library +*/ + +#include "ringbuf.h" + + +/** +* \brief init a RINGBUF object +* \param r pointer to a RINGBUF object +* \param buf pointer to a byte array +* \param size size of buf +* \return 0 if successfull, otherwise failed +*/ +I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size) +{ + if (r == NULL || buf == NULL || size < 2) return -1; + + r->p_o = r->p_r = r->p_w = buf; + r->fill_cnt = 0; + r->size = size; + + return 0; +} +/** +* \brief put a character into ring buffer +* \param r pointer to a ringbuf object +* \param c character to be put +* \return 0 if successfull, otherwise failed +*/ +I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c) +{ + if (r->fill_cnt >= r->size)return -1; // ring buffer is full, this should be atomic operation + + + r->fill_cnt++; // increase filled slots count, this should be atomic operation + + + *r->p_w++ = c; // put character into buffer + + if (r->p_w >= r->p_o + r->size) // rollback if write pointer go pass + r->p_w = r->p_o; // the physical boundary + + return 0; +} +/** +* \brief get a character from ring buffer +* \param r pointer to a ringbuf object +* \param c read character +* \return 0 if successfull, otherwise failed +*/ +I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c) +{ + if (r->fill_cnt <= 0)return -1; // ring buffer is empty, this should be atomic operation + + + r->fill_cnt--; // decrease filled slots count + + + *c = *r->p_r++; // get the character out + + if (r->p_r >= r->p_o + r->size) // rollback if write pointer go pass + r->p_r = r->p_o; // the physical boundary + + return 0; +} diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/ringbuf.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/ringbuf.h new file mode 100644 index 000000000..f1a4f7e8b --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/ringbuf.h @@ -0,0 +1,19 @@ +#ifndef _RING_BUF_H_ +#define _RING_BUF_H_ + +#include +#include +#include "typedef.h" + +typedef struct { + U8* p_o; /**< Original pointer */ + U8* volatile p_r; /**< Read pointer */ + U8* volatile p_w; /**< Write pointer */ + volatile I32 fill_cnt; /**< Number of filled slots */ + I32 size; /**< Buffer size */ +} RINGBUF; + +I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size); +I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c); +I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c); +#endif diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/typedef.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/typedef.h new file mode 100644 index 000000000..887001ace --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/typedef.h @@ -0,0 +1,17 @@ +/** +* \file +* Standard Types definition +*/ + +#ifndef _TYPE_DEF_H_ +#define _TYPE_DEF_H_ + +typedef char I8; +typedef unsigned char U8; +typedef short I16; +typedef unsigned short U16; +typedef long I32; +typedef unsigned long U32; +typedef unsigned long long U64; + +#endif diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/utils.c b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/utils.c new file mode 100644 index 000000000..ac4c9272b --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/utils.c @@ -0,0 +1,149 @@ +/* +* Copyright (c) 2014, Tuan PM +* Email: tuanpm@live.com +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#include +#include +#include +#include +#include +#include "utils.h" + + +uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str) +{ + uint8_t segs = 0; /* Segment count. */ + uint8_t chcnt = 0; /* Character count within segment. */ + uint8_t accum = 0; /* Accumulator for segment. */ + /* Catch NULL pointer. */ + if (str == 0) + return 0; + /* Process every character in string. */ + + while (*str != '\0') { + /* Segment changeover. */ + + if (*str == '.') { + /* Must have some digits in segment. */ + if (chcnt == 0) + return 0; + /* Limit number of segments. */ + if (++segs == 4) + return 0; + /* Reset segment values and restart loop. */ + chcnt = accum = 0; + str++; + continue; + } + + /* Check numeric. */ + if ((*str < '0') || (*str > '9')) + return 0; + + /* Accumulate and check segment. */ + + if ((accum = accum * 10 + *str - '0') > 255) + return 0; + /* Advance other segment specific stuff and continue loop. */ + + chcnt++; + str++; + } + + /* Check enough segments and enough characters in last segment. */ + + if (segs != 3) + return 0; + if (chcnt == 0) + return 0; + /* Address okay. */ + + return 1; +} +uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip) +{ + + /* The count of the number of bytes processed. */ + int i; + /* A pointer to the next digit to process. */ + const char * start; + + start = str; + for (i = 0; i < 4; i++) { + /* The digit being processed. */ + char c; + /* The value of this byte. */ + int n = 0; + while (1) { + c = * start; + start++; + if (c >= '0' && c <= '9') { + n *= 10; + n += c - '0'; + } + /* We insist on stopping at "." if we are still parsing + the first, second, or third numbers. If we have reached + the end of the numbers, we will allow any character. */ + else if ((i < 3 && c == '.') || i == 3) { + break; + } + else { + return 0; + } + } + if (n >= 256) { + return 0; + } + ((uint8_t*)ip)[i] = n; + } + return 1; + +} +uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s) +{ + uint32_t value = 0, digit; + int8_t c; + + while ((c = *s++)) { + if ('0' <= c && c <= '9') + digit = c - '0'; + else if ('A' <= c && c <= 'F') + digit = c - 'A' + 10; + else if ('a' <= c && c <= 'f') + digit = c - 'a' + 10; + else break; + + value = (value << 4) | digit; + } + + return value; +} + diff --git a/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/utils.h b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/utils.h new file mode 100644 index 000000000..fe2874803 --- /dev/null +++ b/lib/esp-mqtt-arduino-1.0.1.02.1/src/mqtt/utils.h @@ -0,0 +1,9 @@ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include "c_types.h" + +uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s); +uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip); +uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str); +#endif diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index b6c50343b..f18747ba0 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,5 +1,5 @@ /* 5.12.0d - * Prep for optional MQTT drivers by separating mqtt code from sonoff.ino to file xdrv_00_mqtt.ino + * Add support for optional MQTT drivers to be selected in user_config.h (#1992) * Add Portuguese language file * Add compiler check for stable lwIP version v1.4 (#1940) * Add diacritics to Polish language file (#2005) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 973a9fca9..099bedd9f 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -221,9 +221,7 @@ char* GetMqttClient(char* output, const char* input, int size) } } } - if (!digits) { - strlcpy(output, input, size); - } + if (!digits) strlcpy(output, input, size); return output; } @@ -270,17 +268,13 @@ void GetTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic) } fulltopic.replace(F("#"), ""); fulltopic.replace(F("//"), "/"); - if (!fulltopic.endsWith("/")) { - fulltopic += "/"; - } + if (!fulltopic.endsWith("/")) fulltopic += "/"; snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram); } char* GetStateText(byte state) { - if (state > 3) { - state = 1; - } + if (state > 3) state = 1; return Settings.state_text[state]; } @@ -315,9 +309,7 @@ void SetDevicePower(power_t rpower) power_t mask = 1; uint8_t count = 0; for (byte i = 0; i < devices_present; i++) { - if (rpower & mask) { - count++; - } + if (rpower & mask) count++; mask <<= 1; } if (count > 1) { @@ -352,9 +344,7 @@ void SetDevicePower(power_t rpower) void SetLedPower(uint8_t state) { - if (state) { - state = 1; - } + if (state) state = 1; digitalWrite(pin[GPIO_LED1], (bitRead(led_inverted, 0)) ? !state : state); } @@ -392,9 +382,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) strncpy(topicBuf, topic, sizeof(topicBuf)); for (i = 0; i < data_len; i++) { - if (!isspace(data[i])) { - break; - } + if (!isspace(data[i])) break; } data_len -= i; memcpy(dataBuf, data +i, sizeof(dataBuf)); @@ -419,9 +407,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) while (isdigit(type[i-1])) { i--; } - if (i < strlen(type)) { - index = atoi(type +i); - } + if (i < strlen(type)) index = atoi(type +i); type[i] = '\0'; } @@ -431,13 +417,9 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) if (type != NULL) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); - if (Settings.ledstate &0x02) { - blinks++; - } + if (Settings.ledstate &0x02) blinks++; - if (!strcmp(dataBuf,"?")) { - data_len = 0; - } + if (!strcmp(dataBuf,"?")) data_len = 0; int16_t payload = -99; // No payload uint16_t payload16 = 0; long lnum = strtol(dataBuf, &p, 10); @@ -489,23 +471,17 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } } else if (CMND_DELAY == command_code) { - if ((payload >= MIN_BACKLOG_DELAY) && (payload <= 3600)) { - backlog_delay = payload; - } + if ((payload >= MIN_BACKLOG_DELAY) && (payload <= 3600)) backlog_delay = payload; snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, backlog_delay); } else if ((CMND_POWER == command_code) && (index > 0) && (index <= devices_present)) { - if ((payload < 0) || (payload > 4)) { - payload = 9; - } + if ((payload < 0) || (payload > 4)) payload = 9; ExecuteCommandPower(index, payload); fallback_topic_flag = 0; return; } else if (CMND_STATUS == command_code) { - if ((payload < 0) || (payload > MAX_STATUS)) { - payload = 99; - } + if ((payload < 0) || (payload > MAX_STATUS)) payload = 99; PublishStatus(payload); fallback_topic_flag = 0; return; @@ -542,18 +518,14 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) else if (CMND_BLINKTIME == command_code) { if ((payload > 2) && (payload <= 3600)) { Settings.blinktime = payload; - if (blink_timer) { - blink_timer = Settings.blinktime; - } + if (blink_timer) blink_timer = Settings.blinktime; } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.blinktime); } else if (CMND_BLINKCOUNT == command_code) { if (data_len > 0) { Settings.blinkcount = payload16; // 0 - 65535 - if (blink_counter) { - blink_counter = Settings.blinkcount *2; - } + if (blink_counter) blink_counter = Settings.blinkcount *2; } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.blinkcount); } @@ -569,15 +541,15 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, (Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); } else if (CMND_SENSOR == command_code) { - XdrvMailbox.topic = command; XdrvMailbox.index = index; - XdrvMailbox.payload = payload; XdrvMailbox.data_len = data_len; + XdrvMailbox.payload16 = payload16; + XdrvMailbox.payload = payload; + XdrvMailbox.grpflg = grpflg; + XdrvMailbox.topic = command; XdrvMailbox.data = dataBuf; XsnsCall(FUNC_COMMAND); -// if (!XsnsCall(FUNC_COMMAND)) { -// type = NULL; -// } +// if (!XsnsCall(FUNC_COMMAND)) type = NULL; } else if ((CMND_SETOPTION == command_code) && ((index <= 21) || ((index > 31) && (index <= P_MAX_PARAM8 + 31)))) { if (index <= 31) { @@ -632,9 +604,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } } } - if (ptype) { - snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), Settings.param[index]); - } + if (ptype) snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), Settings.param[index]); snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, (ptype) ? index +32 : index, (ptype) ? stemp1 : GetStateText(bitRead(Settings.flag.data, index))); } else if (CMND_TEMPERATURE_RESOLUTION == command_code) { @@ -729,9 +699,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) byte jsflg = 0; for (byte i = 0; i < MAX_GPIO_PIN; i++) { if (GPIO_USER == cmodule.gp.io[i]) { - if (jsflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); - } + if (jsflg) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); jsflg = 1; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_GPIO "%d\":\"%d (%s)\""), mqtt_data, i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames)); @@ -769,7 +737,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_PWM "\":{")); bool first = true; for (byte i = 0; i < MAX_PWMS; i++) { - if(pin[GPIO_PWM1 + i] < 99) { + if (pin[GPIO_PWM1 + i] < 99) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_PWM "%d\":%d"), mqtt_data, first ? "" : ",", i+1, Settings.pwm_value[i]); first = false; } @@ -818,9 +786,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } else if (CMND_SLEEP == command_code) { if ((payload >= 0) && (payload < 251)) { - if ((!Settings.sleep && payload) || (Settings.sleep && !payload)) { - restart_flag = 2; - } + if ((!Settings.sleep && payload) || (Settings.sleep && !payload)) restart_flag = 2; Settings.sleep = payload; sleep = payload; } @@ -867,9 +833,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.syslog_host); } else if (CMND_LOGPORT == command_code) { - if (payload16 > 0) { - Settings.syslog_port = (1 == payload16) ? SYS_LOG_PORT : payload16; - } + if (payload16 > 0) Settings.syslog_port = (1 == payload16) ? SYS_LOG_PORT : payload16; snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.syslog_port); } else if ((CMND_IPADDRESS == command_code) && (index > 0) && (index <= 4)) { @@ -884,9 +848,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) if ((data_len > 0) && (data_len < sizeof(Settings.ntp_server[0]))) { strlcpy(Settings.ntp_server[index -1], (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? (1==index)?NTP_SERVER1:(2==index)?NTP_SERVER2:NTP_SERVER3 : dataBuf, sizeof(Settings.ntp_server[0])); for (i = 0; i < strlen(Settings.ntp_server[index -1]); i++) { - if (Settings.ntp_server[index -1][i] == ',') { - Settings.ntp_server[index -1][i] = '.'; - } + if (Settings.ntp_server[index -1][i] == ',') Settings.ntp_server[index -1][i] = '.'; } restart_flag = 2; } @@ -959,16 +921,12 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.friendlyname[index -1]); } else if ((CMND_SWITCHMODE == command_code) && (index > 0) && (index <= MAX_SWITCHES)) { - if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) { - Settings.switchmode[index -1] = payload; - } + if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) Settings.switchmode[index -1] = payload; snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]); } #ifdef USE_WEBSERVER else if (CMND_WEBSERVER == command_code) { - if ((payload >= 0) && (payload <= 2)) { - Settings.webserver = payload; - } + if ((payload >= 0) && (payload <= 2)) Settings.webserver = payload; if (Settings.webserver) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"), (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); @@ -983,9 +941,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.web_password); } else if (CMND_WEBLOG == command_code) { - if ((payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { - Settings.weblog_level = payload; - } + if ((payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) Settings.weblog_level = payload; snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.weblog_level); } #ifdef USE_EMULATION @@ -1001,9 +957,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) else if (CMND_TELEPERIOD == command_code) { if ((payload >= 0) && (payload < 3601)) { Settings.tele_period = (1 == payload) ? TELE_PERIOD : payload; - if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) { - Settings.tele_period = 10; // Do not allow periods < 10 seconds - } + if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; // Do not allow periods < 10 seconds tele_period = Settings.tele_period; } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_UNIT, command, Settings.tele_period, (Settings.flag.value_units) ? " " D_UNIT_SECOND : ""); @@ -1038,15 +992,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } } else if (CMND_TIMEZONE == command_code) { - if ((data_len > 0) && (((payload >= -13) && (payload <= 13)) || (99 == payload))) { - Settings.timezone = payload; - } + if ((data_len > 0) && (((payload >= -13) && (payload <= 13)) || (99 == payload))) Settings.timezone = payload; snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.timezone); } else if (CMND_ALTITUDE == command_code) { - if ((data_len > 0) && ((payload >= -30000) && (payload <= 30000))) { - Settings.altitude = payload; - } + if ((data_len > 0) && ((payload >= -30000) && (payload <= 30000))) Settings.altitude = payload; snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.altitude); } else if (CMND_LEDPOWER == command_code) { @@ -1069,9 +1019,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) else if (CMND_LEDSTATE ==command_code) { if ((payload >= 0) && (payload < MAX_LED_OPTION)) { Settings.ledstate = payload; - if (!Settings.ledstate) { - SetLedPower(0); - } + if (!Settings.ledstate) SetLedPower(0); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.ledstate); } @@ -1084,17 +1032,12 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) I2cScan(mqtt_data, sizeof(mqtt_data)); } #endif // USE_I2C -// else if (Settings.flag.mqtt_enabled && MqttCommand(grpflg, type, index, dataBuf, data_len, payload, payload16)) { - // Serviced -// } else if (XdrvCommand(grpflg, type, index, dataBuf, data_len, payload, payload16)) { // Serviced } #ifdef DEBUG_THEO else if (CMND_EXCEPTION == command_code) { - if (data_len > 0) { - ExceptionTest(payload); - } + if (data_len > 0) ExceptionTest(payload); snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); } #endif // DEBUG_THEO @@ -1108,9 +1051,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); type = (char*)topicBuf; } - if (mqtt_data[0] != '\0') { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); - } + if (mqtt_data[0] != '\0') MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); fallback_topic_flag = 0; } @@ -1132,9 +1073,7 @@ boolean send_button_power(byte key, byte device, byte state) char *key_topic = (key) ? Settings.switch_topic : Settings.button_topic; if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { - if (!key && (device > devices_present)) { - device = 1; - } + if (!key && (device > devices_present)) device = 1; GetTopic_P(stopic, CMND, key_topic, GetPowerDevice(scommand, device, sizeof(scommand), key)); if (9 == state) { mqtt_data[0] = '\0'; @@ -1173,12 +1112,8 @@ void ExecuteCommandPower(byte device, byte state) state &= 1; publish_power = 0; } - if ((device < 1) || (device > devices_present)) { - device = 1; - } - if (device <= MAX_PULSETIMERS) { - pulse_timer[(device -1)] = 0; - } + if ((device < 1) || (device > devices_present)) device = 1; + if (device <= MAX_PULSETIMERS) pulse_timer[(device -1)] = 0; power_t mask = 1 << (device -1); if (state <= POWER_TOGGLE) { if ((blink_mask & mask)) { @@ -1189,9 +1124,7 @@ void ExecuteCommandPower(byte device, byte state) interlock_mutex = 1; for (byte i = 0; i < devices_present; i++) { power_t imask = 1 << i; - if ((power & imask) && (mask != imask)) { - ExecuteCommandPower(i +1, POWER_OFF); - } + if ((power & imask) && (mask != imask)) ExecuteCommandPower(i +1, POWER_OFF); } interlock_mutex = 0; } @@ -1229,14 +1162,10 @@ void ExecuteCommandPower(byte device, byte state) byte flag = (blink_mask & mask); blink_mask &= (POWER_MASK ^ mask); // Clear device mask MqttPublishPowerBlinkState(device); - if (flag) { - ExecuteCommandPower(device, (blink_powersave >> (device -1))&1); // Restore state - } + if (flag) ExecuteCommandPower(device, (blink_powersave >> (device -1))&1); // Restore state return; } - if (publish_power) { - MqttPublishPowerState(device); - } + if (publish_power) MqttPublishPowerState(device); } void StopAllPowerBlink() @@ -1263,9 +1192,7 @@ void ExecuteCommand(char *cmnd) token = strtok(cmnd, " "); if (token != NULL) { start = strrchr(token, '/'); // Skip possible cmnd/sonoff/ preamble - if (start) { - token = start +1; - } + if (start) token = start +1; } snprintf_P(stopic, sizeof(stopic), PSTR("/%s"), (token == NULL) ? "" : token); token = strtok(NULL, ""); @@ -1279,16 +1206,10 @@ void PublishStatus(uint8_t payload) uint8_t option = 1; // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX - if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) { - option++; - } + if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) option++; - if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { - payload = 99; - } - if (!energy_flg && (9 == payload)) { - payload = 99; - } + if ((!Settings.flag.mqtt_enabled) && (6 == payload)) payload = 99; + if (!energy_flg && (9 == payload)) payload = 99; if ((0 == payload) || (99 == payload)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":\"%s\",\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), @@ -1328,8 +1249,8 @@ void PublishStatus(uint8_t payload) } if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), - Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"MqttType\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttLibraryType(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); } @@ -1406,9 +1327,7 @@ boolean MqttShowSensor() } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); - if (json_data_available) { - XdrvCall(FUNC_SHOW_SENSOR); - } + if (json_data_available) XdrvCall(FUNC_SHOW_SENSOR); return json_data_available; } @@ -1418,14 +1337,10 @@ void PerformEverySecond() { uptime++; - if (blockgpio0) { - blockgpio0--; - } + if (blockgpio0) blockgpio0--; for (byte i = 0; i < MAX_PULSETIMERS; i++) { - if (pulse_timer[i] > 111) { - pulse_timer[i]--; - } + if (pulse_timer[i] > 111) pulse_timer[i]--; } if (seriallog_timer) { @@ -1470,10 +1385,7 @@ void PerformEverySecond() MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE)); mqtt_data[0] = '\0'; - if (MqttShowSensor()) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - } - + if (MqttShowSensor()) MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); } } @@ -1485,9 +1397,7 @@ void PerformEverySecond() snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"}"), GetDateAndTime(DT_LOCAL).c_str(), GetDateAndTime(DT_UPTIME).c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_UPTIME)); } - if ((3 == RtcTime.minute) && !latest_uptime_flag) { - latest_uptime_flag = true; - } + if ((3 == RtcTime.minute) && !latest_uptime_flag) latest_uptime_flag = true; } /*********************************************************************************************\ @@ -1525,9 +1435,7 @@ void ButtonHandler() if (button_present) { if (SONOFF_4CHPRO == Settings.module) { - if (holdbutton[button_index]) { - holdbutton[button_index]--; - } + if (holdbutton[button_index]) holdbutton[button_index]--; boolean button_pressed = false; if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { @@ -1539,9 +1447,7 @@ void ButtonHandler() if ((NOT_PRESSED == button) && (PRESSED == lastbutton[button_index])) { snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); AddLog(LOG_LEVEL_DEBUG); - if (!holdbutton[button_index]) { // Do not allow within 1 second - button_pressed = true; - } + if (!holdbutton[button_index]) button_pressed = true; // Do not allow within 1 second } if (button_pressed) { if (!send_button_power(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set @@ -1733,15 +1639,11 @@ void StateLoop() if (!(state % (STATES/10))) { - if (mqtt_cmnd_publish) { - mqtt_cmnd_publish--; // Clean up - } + if (mqtt_cmnd_publish) mqtt_cmnd_publish--; // Clean up if (latching_relay_pulse) { latching_relay_pulse--; - if (!latching_relay_pulse) { - SetLatchingRelay(0, 0); - } + if (!latching_relay_pulse) SetLatchingRelay(0, 0); } for (byte i = 0; i < MAX_PULSETIMERS; i++) { @@ -1770,9 +1672,7 @@ void StateLoop() } // Backlog - if (backlog_delay) { - backlog_delay--; - } + if (backlog_delay) backlog_delay--; if ((backlog_pointer != backlog_index) && !backlog_delay && !backlog_mutex) { backlog_mutex = 1; ExecuteCommand((char*)backlog[backlog_pointer].c_str()); @@ -1813,9 +1713,7 @@ void StateLoop() } if (!blinkstate) { blinks--; - if (200 == blinks) { - blinks = 0; - } + if (200 == blinks) blinks = 0; } } else { if (Settings.ledstate &1) { @@ -1845,9 +1743,7 @@ void StateLoop() } if (ota_state_flag <= 0) { #ifdef USE_WEBSERVER - if (Settings.webserver) { - StopWebserver(); - } + if (Settings.webserver) StopWebserver(); #endif // USE_WEBSERVER #ifdef USE_ARILUX_RF AriluxRfDisable(); // Prevent restart exception on Arilux Interrupt routine @@ -1861,9 +1757,7 @@ void StateLoop() if (RtcSettings.ota_loader) { char *pch = strrchr(mqtt_data, '-'); // Change from filename-DE.bin into filename-minimal.bin char *ech = strrchr(mqtt_data, '.'); // Change from filename.bin into filename-minimal.bin - if (!pch) { - pch = ech; - } + if (!pch) pch = ech; if (pch) { mqtt_data[pch - mqtt_data] = '\0'; char *ech = strrchr(Settings.ota_url, '.'); // Change from filename.bin into filename-minimal.bin @@ -1901,9 +1795,7 @@ void StateLoop() } break; case (STATES/10)*4: - if (MidnightNow()) { - CounterSaveState(); - } + if (MidnightNow()) CounterSaveState(); if (save_data_counter && (backlog_pointer == backlog_index)) { save_data_counter--; if (save_data_counter <= 0) { @@ -1948,6 +1840,9 @@ void StateLoop() WifiCheck(wifi_state_flag); wifi_state_flag = WIFI_RESTART; break; + case (STATES/10)*8: + if (WL_CONNECTED == WiFi.status()) MqttCheck(); + break; } } @@ -2100,14 +1995,10 @@ void GpioInit() } #endif // USE_DHT } - if (mpin) { - pin[mpin] = i; - } + if (mpin) pin[mpin] = i; } - if (2 == pin[GPIO_TXD]) { - Serial.set_tx(2); - } + if (2 == pin[GPIO_TXD]) Serial.set_tx(2); analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) @@ -2116,9 +2007,7 @@ void GpioInit() spi_flg = ((((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CS] > 14)) || (pin[GPIO_SPI_CS] < 12)) || (((pin[GPIO_SPI_DC] < 99) && (pin[GPIO_SPI_DC] > 14)) || (pin[GPIO_SPI_DC] < 12))); if (spi_flg) { for (byte i = 0; i < GPIO_MAX; i++) { - if ((pin[i] >= 12) && (pin[i] <=14)) { - pin[i] = 99; - } + if ((pin[i] >= 12) && (pin[i] <=14)) pin[i] = 99; } my_module.gp.io[12] = GPIO_SPI_MISO; pin[GPIO_SPI_MISO] = 12; @@ -2131,9 +2020,7 @@ void GpioInit() #ifdef USE_I2C i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); - if (i2c_flg) { - Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); - } + if (i2c_flg) Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); #endif // USE_I2C devices_present = 1; @@ -2141,9 +2028,7 @@ void GpioInit() light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 if (Settings.flag.pwm_control) { for (byte i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) { - light_type++; // Use Dimmer/Color control for all PWM as SetOption15 = 1 - } + if (pin[GPIO_PWM1 +i] < 99) light_type++; // Use Dimmer/Color control for all PWM as SetOption15 = 1 } } @@ -2176,9 +2061,7 @@ void GpioInit() light_type = LT_RGBWC; } else { - if (!light_type) { - devices_present = 0; - } + if (!light_type) devices_present = 0; for (byte i = 0; i < MAX_RELAYS; i++) { if (pin[GPIO_REL1 +i] < 99) { pinMode(pin[GPIO_REL1 +i], OUTPUT); @@ -2287,9 +2170,7 @@ void setup() } WifiConnect(); - if (MOTOR == Settings.module) { - Settings.poweronstate = POWER_ALL_ON; // Needs always on else in limbo! - } + if (MOTOR == Settings.module) Settings.poweronstate = POWER_ALL_ON; // Needs always on else in limbo! if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { SetDevicePower(1); } else { diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 9033cc7e5..d2e8aa9e7 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -72,13 +72,23 @@ // -- Ota ----------------------------------------- #define OTA_URL "http://sonoff.maddox.co.uk/tasmota/sonoff.ino.bin" // [OtaUrl] +/*********************************************************************************************\ + * Select ONE of possible MQTT library types below +\*********************************************************************************************/ +// Default MQTT driver for both non-TLS and TLS connections. Blocks network if MQTT server is unavailable. +#define MQTT_LIBRARY_TYPE 1 // Use PubSubClient library +// Alternative MQTT driver does not block network when MQTT server is unavailable. No TLS support +//#define MQTT_LIBRARY_TYPE 2 // Use TasmotaMqtt library (+4k4 code, +4k mem) - non-TLS only +// Alternative MQTT driver does not block network when MQTT server is unavailable. No TLS support +//#define MQTT_LIBRARY_TYPE 3 // Use (patched) esp-mqtt-arduino library (+4k8 code, +4k mem) - non-TLS only + // -- MQTT ---------------------------------------- #define MQTT_USE 1 // [SetOption3] Select default MQTT use (0 = Off, 1 = On) + // !!! TLS uses a LOT OF MEMORY (20k) so be careful to enable other options at the same time !!! -//#define USE_MQTT_TLS // EXPERIMENTAL Use TLS for MQTT connection (+53k code, +15k mem) - Disable by // +//#define USE_MQTT_TLS // Use TLS for MQTT connection (+53k code, +15k mem) - Disable by // // Needs Fingerprint, TLS Port, UserId and Password #ifdef USE_MQTT_TLS -// #define MQTT_HOST "m20.cloudmqtt.com" // [MqttHost] #define MQTT_HOST "" // [MqttHost] #define MQTT_FINGERPRINT "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" // [MqttFingerprint] #define MQTT_PORT 20123 // [MqttPort] MQTT TLS port diff --git a/sonoff/xdrv_00_mqtt.ino b/sonoff/xdrv_00_mqtt.ino index 670dcfe5e..c4196817d 100644 --- a/sonoff/xdrv_00_mqtt.ino +++ b/sonoff/xdrv_00_mqtt.ino @@ -18,17 +18,25 @@ */ /*********************************************************************************************\ - * Select only ONE of the defines below - Not supported yet + * Select ONE of possible MQTT libraries below \*********************************************************************************************/ - // Default MQTT driver for both non-TLS and TLS connections. Blocks network if MQTT server is unavailable. -#define USE_MQTT_CLIENT 1 // Use PubSubClient library +//#define MQTT_LIBRARY_TYPE 1 // Use PubSubClient library +// Alternative MQTT driver does not block network when MQTT server is unavailable. No TLS support +//#define MQTT_LIBRARY_TYPE 2 // Use TasmotaMqtt library (+4k4 code, +4k mem) - non-TLS only +// Alternative MQTT driver does not block network when MQTT server is unavailable. No TLS support +//#define MQTT_LIBRARY_TYPE 3 // Use (patched) esp-mqtt-arduino library (+4k8 code, +4k mem) - non-TLS only -// Replaces PubSubClient solving network hangs when MQTT server is unavailable. No TLS support -//#define USE_MQTT_CLIENT 2 // Use (patched) esp-mqtt-arduino library (+4k8 code) - non-TLS only - -// Only TLS support. Unstable due to lack of memory -//#define USE_MQTT_CLIENT 3 // Use ESP8266MQTTClient library (+52k code, +15k mem) - TLS only and unstable +#ifdef USE_MQTT_TLS +#ifdef MQTT_LIBRARY_TYPE +#undef MQTT_LIBRARY_TYPE +#endif +#define MQTT_LIBRARY_TYPE 1 // Use PubSubClient library as it only supports TLS +#else +#ifndef MQTT_LIBRARY_TYPE +#define MQTT_LIBRARY_TYPE 1 // Use PubSubClient library as default +#endif +#endif /*********************************************************************************************/ @@ -41,8 +49,9 @@ const char kMqttCommands[] PROGMEM = D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ; -uint8_t mqtt_retry_counter = 0; // MQTT connection retry counter -uint8_t mqtt_connection_flag = 2; // MQTT connection messages flag +uint8_t mqtt_retry_counter = 1; // MQTT connection retry counter +uint8_t mqtt_initial_connection_state = 2; // MQTT connection messages state +bool mqtt_connected = false; // MQTT virtual connection status /*********************************************************************************************\ * MQTT driver specific code need to provide the following functions: @@ -51,11 +60,10 @@ uint8_t mqtt_connection_flag = 2; // MQTT connection messages flag * void MqttDisconnect() * void MqttSubscribeLib(char *topic) * bool MqttPublishLib(const char* topic, boolean retained) - * void MqttCheckConnection() * void MqttLoop() \*********************************************************************************************/ -#if (1 == USE_MQTT_CLIENT) /*****************************************************************/ +#if (1 == MQTT_LIBRARY_TYPE) /*****************************************************************/ #include @@ -87,38 +95,53 @@ bool MqttPublishLib(const char* topic, boolean retained) return MqttClient.publish(topic, mqtt_data, retained); } -void MqttCheckConnection() -{ - if (Settings.flag.mqtt_enabled) { - if (!MqttIsConnected()) { - if (!mqtt_retry_counter) { - MqttReconnect(); - } else { - mqtt_retry_counter--; - } - } - } else { - if (!mqtt_retry_counter) { - MqttReconnect(); - } - } -} - void MqttLoop() { MqttClient.loop(); } -#elif (2 == USE_MQTT_CLIENT) /***************************************************************/ +#elif (2 == MQTT_LIBRARY_TYPE) /*****************************************************************/ + +#include +TasmotaMqtt MqttClient; + +bool MqttIsConnected() +{ + return mqtt_connected; +} + +void MqttDisconnect() +{ + MqttClient.Disconnect(); +} + +void MqttDisconnectedCb() +{ + MqttDisconnected(MqttClient.State()); // status codes are documented in file mqtt.h as tConnState +} + +void MqttSubscribeLib(char *topic) +{ + MqttClient.Subscribe(topic, 0); +} + +bool MqttPublishLib(const char* topic, boolean retained) +{ + return MqttClient.Publish(topic, mqtt_data, strlen(mqtt_data), 0, retained); +} + +void MqttLoop() +{ +} + +#elif (3 == MQTT_LIBRARY_TYPE) /***************************************************************/ #include MQTT *MqttClient = NULL; bool MqttIsConnected() { - bool result = false; - if (MqttClient) result = (MqttClient->isConnected() && !mqtt_retry_counter); - return result; + return mqtt_connected; } void MqttDisconnect() @@ -126,6 +149,11 @@ void MqttDisconnect() if (MqttClient) MqttClient->disconnect(); } +void MqttDisconnectedCb() +{ + MqttDisconnected(MqttClient->getState()); // status codes are documented in file mqtt.h as tConnState +} + void MqttMyDataCb(const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { char topicCpy[topic_len]; @@ -134,11 +162,6 @@ void MqttMyDataCb(const char* topic, uint32_t topic_len, const char *data, uint3 MqttDataHandler((char*)topicCpy, (byte*)data, data_len); } -void MqttMyNoneCb() -{ - // Do nothing -} - void MqttSubscribeLib(char *topic) { MqttClient->subscribe(topic); @@ -149,74 +172,19 @@ bool MqttPublishLib(const char* topic, boolean retained) return MqttClient->publish(topic, mqtt_data, strlen(mqtt_data), 0, retained); } -void MqttCheckConnection() -{ - if (Settings.flag.mqtt_enabled) { - if (!MqttIsConnected()) { - if (!mqtt_retry_counter) { - MqttReconnect(); - } else { - mqtt_retry_counter--; - } - } - MqttReconnect(); - } else { - if (!mqtt_retry_counter) { - MqttReconnect(); - } - } -} - void MqttLoop() { - // No action } -#elif (3 == USE_MQTT_CLIENT) /***************************************************************/ - -#include -MQTTClient MqttClient; - -bool MqttIsConnected() -{ - return true; -} - -void MqttDisconnect() -{ - // No action -} - -void MqttSubscribeLib(char *topic) -{ - MqttClient.subscribe(topic, 0); -} - -bool MqttPublishLib(const char* topic, boolean retained) -{ - return MqttClient.publish(topic, mqtt_data, 0, retained); -} - -void MqttCheckConnection() -{ - if (Settings.flag.mqtt_enabled) { - MqttReconnect(); - } else { - if (!mqtt_retry_counter) { - MqttReconnect(); - } - } -} - -void MqttLoop() -{ - MqttClient.handle(); -} - -#endif // USE_MQTT_CLIENT +#endif // MQTT_LIBRARY_TYPE /*********************************************************************************************/ +int MqttLibraryType() +{ + return (int)MQTT_LIBRARY_TYPE; +} + void MqttRetryCounter(uint8_t value) { mqtt_retry_counter = value; @@ -238,10 +206,12 @@ void MqttPublishDirect(const char* topic, boolean retained) snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT)); if (Settings.flag.mqtt_enabled) { - if (MqttPublishLib(topic, retained)) { - snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT)); - if (retained) { - snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")")); + if (MqttIsConnected()) { + if (MqttPublishLib(topic, retained)) { + snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT)); + if (retained) { + snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")")); + } } } } @@ -336,11 +306,28 @@ void MqttPublishPowerBlinkState(byte device) /*********************************************************************************************/ +void MqttDisconnected(int state) +{ + mqtt_connected = false; + mqtt_retry_counter = Settings.mqtt_retry; + + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), + Settings.mqtt_host, Settings.mqtt_port, state, mqtt_retry_counter); + AddLog(LOG_LEVEL_INFO); +} + void MqttConnected() { char stopic[TOPSZ]; if (Settings.flag.mqtt_enabled) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); + mqtt_connected = true; + mqtt_retry_counter = 0; + + GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_ONLINE)); + MqttPublish(stopic, true); // Satisfy iobroker (#299) mqtt_data[0] = '\0'; @@ -360,7 +347,7 @@ void MqttConnected() XdrvCall(FUNC_MQTT_SUBSCRIBE); } - if (mqtt_connection_flag) { + if (mqtt_initial_connection_state) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), my_module.name, my_version, mqtt_client, Settings.mqtt_grptopic); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); @@ -381,48 +368,49 @@ void MqttConnected() XdrvCall(FUNC_MQTT_INIT); } - mqtt_connection_flag = 0; + mqtt_initial_connection_state = 0; } +#ifdef USE_MQTT_TLS +boolean MqttCheckTls() +{ + boolean result = false; + + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_FINGERPRINT)); + if (!EspClient.connect(Settings.mqtt_host, Settings.mqtt_port)) { + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_TLS_CONNECT_FAILED_TO " %s:%d. " D_RETRY_IN " %d " D_UNIT_SECOND), + Settings.mqtt_host, Settings.mqtt_port, mqtt_retry_counter); + AddLog(LOG_LEVEL_DEBUG); + } else if (!EspClient.verify(Settings.mqtt_fingerprint, Settings.mqtt_host)) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_INSECURE)); + } else { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED)); + result = true; + } + EspClient.stop(); + yield(); + return result; +} +#endif // USE_MQTT_TLS + void MqttReconnect() { char stopic[TOPSZ]; - mqtt_retry_counter = Settings.mqtt_retry; - if (!Settings.flag.mqtt_enabled) { MqttConnected(); return; } + #ifdef USE_EMULATION UdpDisconnect(); #endif // USE_EMULATION - if (mqtt_connection_flag > 1) { // Executed once just after power on and wifi is connected - Initial TLS check -#ifdef USE_MQTT_TLS - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_FINGERPRINT)); - if (!EspClient.connect(Settings.mqtt_host, Settings.mqtt_port)) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_TLS_CONNECT_FAILED_TO " %s:%d. " D_RETRY_IN " %d " D_UNIT_SECOND), - Settings.mqtt_host, Settings.mqtt_port, mqtt_retry_counter); - AddLog(LOG_LEVEL_DEBUG); - EspClient.stop(); - return; - } - if (EspClient.verify(Settings.mqtt_fingerprint, Settings.mqtt_host)) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED)); - } else { - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_INSECURE)); - EspClient.stop(); - return; - } - EspClient.stop(); - yield(); -#endif // USE_MQTT_TLS - mqtt_connection_flag = 1; -// mqtt_retry_counter = 1; -// return; - } AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_ATTEMPTING_CONNECTION)); + + mqtt_connected = false; + mqtt_retry_counter = Settings.mqtt_retry; + #ifndef USE_MQTT_TLS #ifdef USE_DISCOVERY #ifdef MQTT_HOST_DISCOVERY @@ -441,63 +429,57 @@ void MqttReconnect() GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); snprintf_P(mqtt_data, sizeof(mqtt_data), S_OFFLINE); -#if (1 == USE_MQTT_CLIENT) + if (2 == mqtt_initial_connection_state) { // Executed once just after power on and wifi is connected +#ifdef USE_MQTT_TLS + if (!MqttCheckTls()) return; +#endif // USE_MQTT_TLS + +#if (2 == MQTT_LIBRARY_TYPE) + MqttClient.InitConnection(Settings.mqtt_host, Settings.mqtt_port); + MqttClient.InitClient(mqtt_client, mqtt_user, mqtt_pwd, MQTT_KEEPALIVE); + MqttClient.InitLWT(stopic, mqtt_data, 1, true); + MqttClient.OnConnected(MqttConnected); + MqttClient.OnDisconnected(MqttDisconnectedCb); + MqttClient.OnData(MqttDataHandler); +#elif (3 == MQTT_LIBRARY_TYPE) + MqttClient = new MQTT(mqtt_client, Settings.mqtt_host, Settings.mqtt_port, stopic, 1, true, mqtt_data); + MqttClient->setUserPwd(mqtt_user, mqtt_pwd); + MqttClient->onConnected(MqttConnected); + MqttClient->onDisconnected(MqttDisconnectedCb); + MqttClient->onData(MqttMyDataCb); +#endif + + mqtt_initial_connection_state = 1; + } + +#if (1 == MQTT_LIBRARY_TYPE) MqttClient.setCallback(MqttDataHandler); MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port); if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, true, mqtt_data)) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); - mqtt_retry_counter = 0; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_ONLINE)); - MqttPublish(stopic, true); MqttConnected(); } else { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), - Settings.mqtt_host, Settings.mqtt_port, MqttClient.state(), mqtt_retry_counter); //status codes are documented here http://pubsubclient.knolleary.net/api.html#state - AddLog(LOG_LEVEL_INFO); - } -#elif (2 == USE_MQTT_CLIENT) - if (!MqttClient) { - MqttClient = new MQTT(mqtt_client, Settings.mqtt_host, Settings.mqtt_port, stopic, 1, true, mqtt_data); -// MqttClient->onConnected(MqttMyNoneCb); -// MqttClient->onDisconnected(MqttMyNoneCb); -// MqttClient->onPublished(MqttMyNoneCb); - MqttClient->onData(MqttMyDataCb); - MqttClient->setUserPwd(mqtt_user, mqtt_pwd); + MqttDisconnected(MqttClient.state()); // status codes are documented here http://pubsubclient.knolleary.net/api.html#state } +#elif (2 == MQTT_LIBRARY_TYPE) + MqttClient.Connect(); +#elif (3 == MQTT_LIBRARY_TYPE) MqttClient->connect(); - uint16_t mqtt_timeout = 50; - while (!MqttClient->isConnected() && mqtt_timeout--) delay(1); - if (MqttClient->isConnected()) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); - mqtt_retry_counter = 0; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_ONLINE)); - MqttPublish(stopic, true); - MqttConnected(); - } else { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d. " D_RETRY_IN " %d " D_UNIT_SECOND), - Settings.mqtt_host, Settings.mqtt_port, mqtt_retry_counter); - AddLog(LOG_LEVEL_INFO); - } -#elif (3 == USE_MQTT_CLIENT) - //topic, data, data is continuing - MqttClient.onData([](String topic, String data, bool cont) { - MqttDataHandler((char*)topic.c_str(), (byte*)data.c_str(), strlen(data.c_str())); - }); - char uri[200]; - snprintf_P(uri, sizeof(uri), PSTR("mqtt://%s:%s@%s:%d#%s"), mqtt_user, mqtt_pwd, Settings.mqtt_host, Settings.mqtt_port, mqtt_client); +#endif // MQTT_LIBRARY_TYPE +} - if (MqttClient.begin(String(uri), {.lwtTopic = stopic, .lwtMsg = mqtt_data, .lwtQos = 1, .lwtRetain = 1}, MQTT_KEEPALIVE, true)) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); - mqtt_retry_counter = 0; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_ONLINE)); - MqttPublish(stopic, true); - MqttConnected(); +void MqttCheck() +{ + if (Settings.flag.mqtt_enabled) { + if (!MqttIsConnected()) { + if (!mqtt_retry_counter) { + MqttReconnect(); + } else { + mqtt_retry_counter--; + } + } } else { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d. " D_RETRY_IN " %d " D_UNIT_SECOND), - Settings.mqtt_host, Settings.mqtt_port, mqtt_retry_counter); //status codes are documented here http://pubsubclient.knolleary.net/api.html#state - AddLog(LOG_LEVEL_INFO); + if (mqtt_initial_connection_state) MqttReconnect(); } -#endif // USE_MQTT_CLIENT } /*********************************************************************************************/ @@ -543,9 +525,7 @@ bool MqttCommand() else if ((CMND_STATETEXT == command_code) && (index > 0) && (index <= 4)) { if ((data_len > 0) && (data_len < sizeof(Settings.state_text[0]))) { for(i = 0; i <= data_len; i++) { - if (dataBuf[i] == ' ') { - dataBuf[i] = '_'; - } + if (dataBuf[i] == ' ') dataBuf[i] = '_'; } strlcpy(Settings.state_text[index -1], dataBuf, sizeof(Settings.state_text[0])); } @@ -584,9 +564,7 @@ bool MqttCommand() else if (CMND_FULLTOPIC == command_code) { if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_fulltopic))) { MakeValidMqtt(1, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) { - payload = 1; - } + if (!strcmp(dataBuf, mqtt_client)) payload = 1; strlcpy(stemp1, (1 == payload) ? MQTT_FULLTOPIC : dataBuf, sizeof(stemp1)); if (strcmp(stemp1, Settings.mqtt_fulltopic)) { snprintf_P(mqtt_data, sizeof(mqtt_data), (Settings.flag.mqtt_offline) ? S_OFFLINE : ""); @@ -609,9 +587,7 @@ bool MqttCommand() else if (CMND_GROUPTOPIC == command_code) { if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_grptopic))) { MakeValidMqtt(0, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) { - payload = 1; - } + if (!strcmp(dataBuf, mqtt_client)) payload = 1; strlcpy(Settings.mqtt_grptopic, (1 == payload) ? MQTT_GRPTOPIC : dataBuf, sizeof(Settings.mqtt_grptopic)); restart_flag = 2; } @@ -620,9 +596,7 @@ bool MqttCommand() else if ((CMND_TOPIC == command_code) && !grpflg) { if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_topic))) { MakeValidMqtt(0, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) { - payload = 1; - } + if (!strcmp(dataBuf, mqtt_client)) payload = 1; strlcpy(stemp1, (1 == payload) ? MQTT_TOPIC : dataBuf, sizeof(stemp1)); if (strcmp(stemp1, Settings.mqtt_topic)) { snprintf_P(mqtt_data, sizeof(mqtt_data), (Settings.flag.mqtt_offline) ? S_OFFLINE : ""); @@ -636,9 +610,7 @@ bool MqttCommand() else if ((CMND_BUTTONTOPIC == command_code) && !grpflg) { if ((data_len > 0) && (data_len < sizeof(Settings.button_topic))) { MakeValidMqtt(0, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) { - payload = 1; - } + if (!strcmp(dataBuf, mqtt_client)) payload = 1; strlcpy(Settings.button_topic, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? mqtt_topic : dataBuf, sizeof(Settings.button_topic)); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.button_topic); @@ -646,9 +618,7 @@ bool MqttCommand() else if (CMND_SWITCHTOPIC == command_code) { if ((data_len > 0) && (data_len < sizeof(Settings.switch_topic))) { MakeValidMqtt(0, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) { - payload = 1; - } + if (!strcmp(dataBuf, mqtt_client)) payload = 1; strlcpy(Settings.switch_topic, (!strcmp(dataBuf,"0")) ? "" : (1 == payload) ? mqtt_topic : dataBuf, sizeof(Settings.switch_topic)); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.switch_topic); @@ -715,16 +685,15 @@ boolean Xdrv00(byte function) { boolean result = false; - switch (function) { - case FUNC_LOOP: - if (Settings.flag.mqtt_enabled) MqttLoop(); - break; - case FUNC_EVERY_SECOND: - if (WL_CONNECTED == WiFi.status()) MqttCheckConnection(); - break; - case FUNC_COMMAND: - if (Settings.flag.mqtt_enabled) result = MqttCommand(); - break; + if (Settings.flag.mqtt_enabled) { + switch (function) { + case FUNC_LOOP: + MqttLoop(); + break; + case FUNC_COMMAND: + result = MqttCommand(); + break; + } } return result; } diff --git a/sonoff/xdrv_interface.ino b/sonoff/xdrv_interface.ino index 15df24a13..09f056ece 100644 --- a/sonoff/xdrv_interface.ino +++ b/sonoff/xdrv_interface.ino @@ -179,6 +179,7 @@ boolean XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t * Function call to all xdrv * * FUNC_INIT + * FUNC_LOOP * FUNC_MQTT_SUBSCRIBE * FUNC_MQTT_INIT * return FUNC_MQTT_DATA @@ -195,9 +196,7 @@ boolean XdrvCall(byte Function) for (byte x = 0; x < xdrv_present; x++) { result = xdrv_func_ptr[x](Function); - if (result) { - break; - } + if (result) break; } return result; diff --git a/sonoff/xsns_interface.ino b/sonoff/xsns_interface.ino index 02e380a95..20488dc92 100644 --- a/sonoff/xsns_interface.ino +++ b/sonoff/xsns_interface.ino @@ -206,9 +206,7 @@ boolean XsnsCall(byte Function) for (byte x = 0; x < xsns_present; x++) { result = xsns_func_ptr[x](Function); - if (result) { - break; - } + if (result) break; } return result;