Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
7.3 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. /*
  2. MQTT MODULE
  3. Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #include <ESP8266WiFi.h>
  6. #include <vector>
  7. #if MQTT_USE_ASYNC
  8. #include <AsyncMqttClient.h>
  9. AsyncMqttClient mqtt;
  10. #else
  11. #include <PubSubClient.h>
  12. WiFiClient mqttWiFiClient;
  13. PubSubClient mqtt(mqttWiFiClient);
  14. bool _mqttConnected = false;
  15. #endif
  16. String mqttTopic;
  17. std::vector<void (*)(unsigned int, const char *, const char *)> _mqtt_callbacks;
  18. #if MQTT_SKIP_RETAINED
  19. unsigned long mqttConnectedAt = 0;
  20. #endif
  21. // -----------------------------------------------------------------------------
  22. // Public API
  23. // -----------------------------------------------------------------------------
  24. bool mqttConnected() {
  25. return mqtt.connected();
  26. }
  27. void mqttDisconnect() {
  28. mqtt.disconnect();
  29. }
  30. void buildTopics() {
  31. // Replace identifier
  32. mqttTopic = getSetting("mqttTopic", MQTT_TOPIC);
  33. mqttTopic.replace("{identifier}", getSetting("hostname"));
  34. }
  35. char * mqttSubtopic(char * topic) {
  36. int pos = min(mqttTopic.length(), strlen(topic));
  37. return topic + pos;
  38. }
  39. void mqttSendRaw(const char * topic, const char * message) {
  40. if (mqtt.connected()) {
  41. DEBUG_MSG("[MQTT] Sending %s => %s\n", topic, message);
  42. #if MQTT_USE_ASYNC
  43. mqtt.publish(topic, MQTT_QOS, MQTT_RETAIN, message);
  44. #else
  45. mqtt.publish(topic, message, MQTT_RETAIN);
  46. #endif
  47. }
  48. }
  49. void mqttSend(const char * topic, const char * message) {
  50. String path = mqttTopic + String(topic);
  51. mqttSendRaw(path.c_str(), message);
  52. }
  53. void mqttSubscribeRaw(const char * topic) {
  54. if (mqtt.connected()) {
  55. DEBUG_MSG("[MQTT] Subscribing to %s\n", topic);
  56. mqtt.subscribe(topic, MQTT_QOS);
  57. }
  58. }
  59. void mqttSubscribe(const char * topic) {
  60. String path = mqttTopic + String(topic);
  61. mqttSubscribeRaw(path.c_str());
  62. }
  63. // -----------------------------------------------------------------------------
  64. // Callbacks
  65. // -----------------------------------------------------------------------------
  66. void mqttRegister(void (*callback)(unsigned int, const char *, const char *)) {
  67. _mqtt_callbacks.push_back(callback);
  68. }
  69. void _mqttOnConnect() {
  70. DEBUG_MSG("[MQTT] Connected!\n");
  71. #if MQTT_SKIP_RETAINED
  72. mqttConnectedAt = millis();
  73. #endif
  74. // Build MQTT topics
  75. buildTopics();
  76. // Say hello and report our IP and VERSION
  77. mqttSend(MQTT_IP_TOPIC, getIP().c_str());
  78. mqttSend(MQTT_VERSION_TOPIC, APP_VERSION);
  79. // Subscribe to system topics
  80. mqttSubscribe(MQTT_ACTION_TOPIC);
  81. // Send connect event to subscribers
  82. for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
  83. (*_mqtt_callbacks[i])(MQTT_CONNECT_EVENT, NULL, NULL);
  84. }
  85. }
  86. void _mqttOnDisconnect() {
  87. DEBUG_MSG("[MQTT] Disconnected!\n");
  88. // Send disconnect event to subscribers
  89. for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
  90. (*_mqtt_callbacks[i])(MQTT_DISCONNECT_EVENT, NULL, NULL);
  91. }
  92. }
  93. void _mqttOnMessage(char* topic, char* payload, unsigned int len) {
  94. char message[len + 1];
  95. strlcpy(message, (char *) payload, len + 1);
  96. DEBUG_MSG("[MQTT] Received %s => %s", topic, message);
  97. #if MQTT_SKIP_RETAINED
  98. if (millis() - mqttConnectedAt < MQTT_SKIP_TIME) {
  99. DEBUG_MSG(" - SKIPPED\n");
  100. return;
  101. }
  102. #endif
  103. DEBUG_MSG("\n");
  104. // Check system topics
  105. char * p = mqttSubtopic(topic);
  106. if (strcmp(p, MQTT_ACTION_TOPIC) == 0) {
  107. if (strcmp(message, MQTT_ACTION_RESET) == 0) {
  108. ESP.reset();
  109. }
  110. }
  111. // Send message event to subscribers
  112. // Topic is set to the specific part each one might be checking
  113. for (unsigned char i = 0; i < _mqtt_callbacks.size(); i++) {
  114. (*_mqtt_callbacks[i])(MQTT_MESSAGE_EVENT, topic, message);
  115. }
  116. }
  117. void mqttConnect() {
  118. if (!mqtt.connected()) {
  119. if (getSetting("mqttServer", MQTT_SERVER).length() == 0) return;
  120. // Last option: reconnect to wifi after MQTT_MAX_TRIES attemps in a row
  121. #if MQTT_MAX_TRIES > 0
  122. static unsigned int tries = 0;
  123. static unsigned long last_try = millis();
  124. if (millis() - last_try < MQTT_TRY_INTERVAL) {
  125. if (++tries > MQTT_MAX_TRIES) {
  126. DEBUG_MSG("[MQTT] MQTT_MAX_TRIES met, disconnecting from WiFi\n");
  127. wifiDisconnect();
  128. tries = 0;
  129. return;
  130. }
  131. } else {
  132. tries = 0;
  133. }
  134. last_try = millis();
  135. #endif
  136. mqtt.disconnect();
  137. char * host = strdup(getSetting("mqttServer", MQTT_SERVER).c_str());
  138. unsigned int port = getSetting("mqttPort", MQTT_PORT).toInt();
  139. char * user = strdup(getSetting("mqttUser").c_str());
  140. char * pass = strdup(getSetting("mqttPassword").c_str());
  141. DEBUG_MSG("[MQTT] Connecting to broker at %s", host);
  142. mqtt.setServer(host, port);
  143. #if MQTT_USE_ASYNC
  144. mqtt.setKeepAlive(MQTT_KEEPALIVE).setCleanSession(false);
  145. mqtt.setWill((mqttTopic + MQTT_HEARTBEAT_TOPIC).c_str(), MQTT_QOS, MQTT_RETAIN, "0");
  146. if ((strlen(user) > 0) && (strlen(pass) > 0)) {
  147. DEBUG_MSG(" as user '%s'.", user);
  148. mqtt.setCredentials(user, pass);
  149. }
  150. DEBUG_MSG("\n");
  151. mqtt.connect();
  152. #else
  153. bool response;
  154. if ((strlen(user) > 0) && (strlen(pass) > 0)) {
  155. DEBUG_MSG(" as user '%s'\n", user);
  156. response = mqtt.connect(getIdentifier().c_str(), user, pass, (mqttTopic + MQTT_HEARTBEAT_TOPIC).c_str(), MQTT_QOS, MQTT_RETAIN, "0");
  157. } else {
  158. DEBUG_MSG("\n");
  159. response = mqtt.connect(getIdentifier().c_str(), (mqttTopic + MQTT_HEARTBEAT_TOPIC).c_str(), MQTT_QOS, MQTT_RETAIN, "0");
  160. }
  161. if (response) {
  162. _mqttOnConnect();
  163. _mqttConnected = true;
  164. } else {
  165. DEBUG_MSG("[MQTT] Connection failed\n");
  166. }
  167. #endif
  168. free(host);
  169. free(user);
  170. free(pass);
  171. }
  172. }
  173. void mqttSetup() {
  174. #if MQTT_USE_ASYNC
  175. mqtt.onConnect([](bool sessionPresent) {
  176. _mqttOnConnect();
  177. });
  178. mqtt.onDisconnect([](AsyncMqttClientDisconnectReason reason) {
  179. _mqttOnDisconnect();
  180. });
  181. mqtt.onMessage([](char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
  182. _mqttOnMessage(topic, payload, len);
  183. });
  184. #else
  185. mqtt.setCallback([](char* topic, byte* payload, unsigned int length) {
  186. _mqttOnMessage(topic, (char *) payload, length);
  187. });
  188. #endif
  189. buildTopics();
  190. }
  191. void mqttLoop() {
  192. static unsigned long lastPeriod = 0;
  193. if (WiFi.status() == WL_CONNECTED) {
  194. if (!mqtt.connected()) {
  195. #if not MQTT_USE_ASYNC
  196. if (_mqttConnected) {
  197. _mqttOnDisconnect();
  198. _mqttConnected = false;
  199. }
  200. #endif
  201. unsigned long currPeriod = millis() / MQTT_RECONNECT_DELAY;
  202. if (currPeriod != lastPeriod) {
  203. lastPeriod = currPeriod;
  204. mqttConnect();
  205. }
  206. #if not MQTT_USE_ASYNC
  207. } else {
  208. mqtt.loop();
  209. #endif
  210. }
  211. }
  212. }